Skip to content

Commit 679c3fe

Browse files
Merge pull request #4769 from OneCommunityGlobal/julia-log-owner-message-change
Julia - Create owner message change history
2 parents 3f566e2 + 240e9ce commit 679c3fe

9 files changed

Lines changed: 249 additions & 3 deletions

File tree

public/index.css

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,12 @@ body.bm-dashboard-dark .page-item.active .page-link {
308308
color: #ffffff !important;
309309
}
310310

311+
body.dark-mode .page-item.disabled .page-link {
312+
background-color: #6c6c6c;
313+
border-color: #7c7c7c;
314+
color: #ffffff !important;
315+
}
316+
311317
/* Fix the position of the header at the top */
312318
.back-to-top {
313319
position: fixed;

src/actions/ownerMessageAction.js

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,13 @@ export const updateOwnerMessageAction = payload => {
1111
};
1212
};
1313

14+
export const updateOwnerMessageHistoryAction = payload => {
15+
return {
16+
type: types.UPDATE_OWNER_MESSAGE_HISTORY,
17+
payload,
18+
};
19+
};
20+
1421
// redux thunk functions
1522
export const getOwnerMessage = () => {
1623
const url = ENDPOINTS.OWNERMESSAGE();
@@ -33,6 +40,8 @@ export const updateOwnerMessage = newMessage => {
3340
const response = await axios.put(url, newMessage);
3441
const { ownerMessage } = response.data;
3542
dispatch(updateOwnerMessageAction(ownerMessage));
43+
// refresh history after update
44+
dispatch(getOwnerMessageHistory());
3645
return response;
3746
} catch (error) {
3847
return error.response.data.error;
@@ -47,6 +56,23 @@ export const deleteOwnerMessage = () => {
4756
const response = await axios.delete(url);
4857
const { ownerMessage } = response.data;
4958
dispatch(updateOwnerMessageAction(ownerMessage));
59+
// refresh history after update
60+
dispatch(getOwnerMessageHistory());
61+
return response;
62+
} catch (error) {
63+
return error.response.data.error;
64+
}
65+
};
66+
};
67+
68+
export const getOwnerMessageHistory = (page = 1, limit = 10) => {
69+
const url = ENDPOINTS.OWNER_MESSAGE_HISTORY(page, limit);
70+
71+
return async dispatch => {
72+
try {
73+
const response = await axios.get(url);
74+
const ownerMessageHistory = response.data;
75+
dispatch(updateOwnerMessageHistoryAction(ownerMessageHistory));
5076
return response;
5177
} catch (error) {
5278
return error.response.data.error;

src/components/OwnerMessage/OwnerMessage.jsx

Lines changed: 156 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,17 @@
11
import { useState, useEffect } from 'react';
22
import { toast } from 'react-toastify';
3-
import { Button, Modal, ModalHeader, ModalBody, ModalFooter, Input } from 'reactstrap';
3+
import {
4+
Button,
5+
Modal,
6+
ModalHeader,
7+
ModalBody,
8+
ModalFooter,
9+
Input,
10+
Table,
11+
Pagination,
12+
PaginationItem,
13+
PaginationLink,
14+
} from 'reactstrap';
415
import { connect, useDispatch } from 'react-redux';
516
import hasPermission from '~/utils/permissions';
617
import { boxStyle, boxStyleDark } from '../../styles';
@@ -14,16 +25,19 @@ import {
1425
getOwnerMessage,
1526
updateOwnerMessage,
1627
deleteOwnerMessage,
28+
getOwnerMessageHistory,
1729
} from '../../actions/ownerMessageAction';
1830

1931
function OwnerMessage({
2032
auth,
2133
ownerMessage,
2234
ownerStandardMessage,
35+
ownerMessageHistory,
2336
darkMode,
2437
getMessage,
2538
updateMessage,
2639
deleteMessage,
40+
getMessageHistory,
2741
}) {
2842
const dispatch = useDispatch();
2943
const { user } = auth;
@@ -32,6 +46,7 @@ function OwnerMessage({
3246
const [disableButtons, setDisableButtons] = useState(true);
3347
const [message, setMessage] = useState('');
3448
const [modal, setModal] = useState(false);
49+
const [historyModalOpen, setHistoryModalOpen] = useState(false);
3550
const [modalDeleteWarning, setModalDeleteWarning] = useState(false);
3651
const [modalWrongPictureFormatWarning, setModalWrongPictureFormatWarning] = useState(false);
3752

@@ -107,9 +122,33 @@ function OwnerMessage({
107122
return <span className={styles.message}>{messages}</span>;
108123
}
109124

125+
function getHistoryContent(messages) {
126+
if (isImage.test(messages)) {
127+
return <img src={messages} alt="" className={styles.ownerMessageImg} />;
128+
}
129+
return <span>{messages}</span>;
130+
}
131+
132+
const formatDateTimePST = date =>
133+
new Intl.DateTimeFormat('en-US', {
134+
timeZone: 'America/Los_Angeles',
135+
year: 'numeric',
136+
month: 'short',
137+
day: 'numeric',
138+
hour: '2-digit',
139+
minute: '2-digit',
140+
hour12: true,
141+
}).format(new Date(date));
142+
143+
const page = ownerMessageHistory?.pagination?.page ? ownerMessageHistory.pagination.page : 1;
144+
const totalPages = ownerMessageHistory?.pagination?.totalPages
145+
? ownerMessageHistory.pagination.totalPages
146+
: 1;
147+
110148
useEffect(() => {
111149
async function fetchMessages() {
112150
await getMessage();
151+
await getMessageHistory(page, 10);
113152
}
114153
fetchMessages();
115154
}, []);
@@ -177,6 +216,22 @@ function OwnerMessage({
177216
/>
178217
</button>
179218
)}
219+
<button
220+
type="submit"
221+
className={styles.ownerMessageButton}
222+
onClick={() => setHistoryModalOpen(true)}
223+
aria-label="View owner message edit history"
224+
>
225+
<i
226+
style={{
227+
fontSize: '24px',
228+
marginLeft: '0.25rem',
229+
color: 'white',
230+
}}
231+
className="fa fa-history"
232+
aria-label="View owner message edit history"
233+
></i>
234+
</button>
180235
</span>
181236
)}
182237

@@ -271,6 +326,104 @@ function OwnerMessage({
271326
</Button>
272327
</ModalFooter>
273328
</Modal>
329+
330+
{/* Owner Edit History Modal */}
331+
<Modal
332+
size="xl"
333+
isOpen={historyModalOpen}
334+
toggle={() => setHistoryModalOpen(false)}
335+
className={`${fontColor}`}
336+
>
337+
<ModalHeader toggle={() => setHistoryModalOpen(false)}>
338+
Owner Message Edit History
339+
</ModalHeader>
340+
<ModalBody className={headerBg}>
341+
{!ownerMessageHistory?.data?.length ? (
342+
<p>No edit history available.</p>
343+
) : (
344+
<>
345+
<Table className={styles.ownerHistoryTable}>
346+
<thead className={`${darkMode ? 'bg-space-cadet' : ''}`}>
347+
<tr>
348+
<th
349+
className={`${styles.ownerHistorySmallColumn} ${
350+
darkMode ? 'bg-space-cadet' : ''
351+
}`}
352+
>
353+
Date
354+
</th>
355+
<th
356+
className={`${styles.ownerHistorySmallColumn} ${
357+
darkMode ? 'bg-space-cadet' : ''
358+
}`}
359+
>
360+
Edited By
361+
</th>
362+
<th className={`${darkMode ? 'bg-space-cadet' : ''}`}>Action</th>
363+
<th className={`${darkMode ? 'bg-space-cadet' : ''}`}>Old Message</th>
364+
<th className={`${darkMode ? 'bg-space-cadet' : ''}`}>New Message</th>
365+
</tr>
366+
</thead>
367+
<tbody className={darkMode ? 'bg-yinmn-blue dark-mode' : ''}>
368+
{ownerMessageHistory.data.map((historyItem, index) => (
369+
<tr key={index}>
370+
<td className={styles.ownerHistorySmallColumn}>
371+
<span className={styles.showInTablet}>
372+
<b>Date:</b>
373+
</span>
374+
{formatDateTimePST(historyItem.createdAt)} PST
375+
</td>
376+
377+
<td className={styles.ownerHistorySmallColumn}>
378+
<span className={styles.showInTablet}>
379+
<b>Edited By:</b>
380+
</span>
381+
{historyItem.requestorName} ({historyItem.requestorEmail})
382+
</td>
383+
<td>
384+
<span className={styles.showInTablet}>
385+
<b>Action:</b>
386+
</span>
387+
{historyItem.action}
388+
</td>
389+
<td>
390+
<span className={styles.showInTablet}>
391+
<b>Old Message:</b>
392+
</span>
393+
{getHistoryContent(historyItem.oldMessage)}
394+
</td>
395+
<td>
396+
<span className={styles.showInTablet}>
397+
<b>New Message:</b>
398+
</span>
399+
{getHistoryContent(historyItem.newMessage)}
400+
</td>
401+
</tr>
402+
))}
403+
</tbody>
404+
</Table>
405+
<Pagination aria-label="Table pagination">
406+
<PaginationItem disabled={page === 1}>
407+
<PaginationLink previous onClick={() => getMessageHistory(page - 1, 10)} />
408+
</PaginationItem>
409+
410+
<PaginationItem active>
411+
<PaginationLink>{page}</PaginationLink>
412+
</PaginationItem>
413+
414+
<PaginationItem disabled={page === totalPages}>
415+
<PaginationLink next onClick={() => getMessageHistory(page + 1, 10)} />
416+
</PaginationItem>
417+
</Pagination>
418+
</>
419+
)}
420+
</ModalBody>
421+
<ModalFooter className={bodyBg}>
422+
<Button color="secondary" onClick={() => setHistoryModalOpen(false)} style={boxStyling}>
423+
Close
424+
</Button>
425+
</ModalFooter>
426+
</Modal>
274427
</div>
275428
);
276429
}
@@ -280,12 +433,14 @@ const mapStateToProps = state => ({
280433
ownerMessage: state.ownerMessage.message,
281434
ownerStandardMessage: state.ownerMessage.standardMessage,
282435
darkMode: state.theme.darkMode,
436+
ownerMessageHistory: state.ownerMessage.history,
283437
});
284438

285439
const mapDispatchToProps = dispatch => ({
286440
getMessage: () => dispatch(getOwnerMessage()),
287441
updateMessage: ownerMessage => dispatch(updateOwnerMessage(ownerMessage)),
288442
deleteMessage: () => dispatch(deleteOwnerMessage()),
443+
getMessageHistory: (page, limit) => dispatch(getOwnerMessageHistory(page, limit)),
289444
});
290445

291446
export default connect(mapStateToProps, mapDispatchToProps)(OwnerMessage);

src/components/OwnerMessage/OwnerMessage.module.css

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,50 @@
6666
flex-direction: column;
6767
}
6868

69+
.ownerHistorySmallColumn {
70+
max-width: 150px;
71+
word-wrap: break-word;
72+
}
73+
74+
.ownerHistoryTable {
75+
border-collapse: collapse;
76+
}
77+
78+
.ownerHistoryTable th, .ownerHistoryTable td {
79+
border-bottom: 1px solid #858585 !important;
80+
}
81+
82+
.showInTablet {
83+
display: none;
84+
}
85+
86+
@media (max-width: 990px) {
87+
.ownerHistorySmallColumn {
88+
max-width: 100%;
89+
word-wrap: break-word;
90+
}
91+
.ownerHistoryTable thead {
92+
display: none;
93+
}
94+
.ownerHistoryTable .table,
95+
.ownerHistoryTable tbody,
96+
.ownerHistoryTable tr,
97+
.ownerHistoryTable td {
98+
display: block;
99+
width: 100%;
100+
text-align: left !important;
101+
}
102+
.ownerHistoryTable th, .ownerHistoryTable td {
103+
border-bottom: none !important;
104+
}
105+
.ownerHistoryTable tr {
106+
border-bottom: 1px solid #858585 !important;
107+
}
108+
.showInTablet {
109+
display: block;
110+
}
111+
}
112+
69113
@media (max-width: 768px) {
70114
.ownerMessageImg {
71115
height: 36px;

src/constants/ownerMessageConstants.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@ export const GET_OWNER_MESSAGE = 'GET_OWNER_MESSAGE';
22
export const CREATE_OWNER_MESSAGE = 'CREATE_OWNER_MESSAGE';
33
export const UPDATE_OWNER_MESSAGE = 'UPDATE_OWNER_MESSAGE';
44
export const DELETE_OWNER_MESSAGE = 'DELETE_OWNER_MESSAGE';
5+
export const UPDATE_OWNER_MESSAGE_HISTORY = 'UPDATE_OWNER_MESSAGE_HISTORY';

src/reducers/__tests__/ownerMessageReducer.test.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ describe('ownerMessageReducer', () => {
55
const initialState = {
66
message: '',
77
standardMessage: '',
8+
history: [],
89
};
910

1011
it('should return the initial state when no action is provided', () => {
@@ -23,6 +24,7 @@ describe('ownerMessageReducer', () => {
2324
const expectedState = {
2425
message: 'Updated owner message',
2526
standardMessage: 'This is the standard message',
27+
history: [],
2628
};
2729

2830
expect(ownerMessageReducer(initialState, action)).toEqual(expectedState);

src/reducers/ownerMessageReducer.js

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,24 @@ import * as types from '../constants/ownerMessageConstants';
33
const initialState = {
44
message: '',
55
standardMessage: '',
6+
history: [],
67
};
78

89
// eslint-disable-next-line default-param-last
910
export const ownerMessageReducer = (state = initialState, action) => {
1011
switch (action.type) {
1112
case types.UPDATE_OWNER_MESSAGE:
12-
return action.payload;
13+
return {
14+
...state,
15+
message: action.payload.message,
16+
standardMessage: action.payload.standardMessage,
17+
};
18+
19+
case types.UPDATE_OWNER_MESSAGE_HISTORY:
20+
return {
21+
...state,
22+
history: action.payload,
23+
};
1324

1425
default:
1526
return state;

src/utils/URL.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,7 @@ export const ENDPOINTS = {
228228
PRESETS_BY_ID: roleNameOrPresetId => `${APIEndpoint}/rolePreset/${roleNameOrPresetId}`,
229229

230230
OWNERMESSAGE: () => `${APIEndpoint}/ownerMessage`,
231+
OWNER_MESSAGE_HISTORY: (page, limit) => `${APIEndpoint}/ownerMessageLogs?page=${page}&limit=${limit}`,
231232

232233
AI_PROMPT: () => `${APIEndpoint}/dashboard/aiPrompt`,
233234
COPIED_AI_PROMPT: userId => `${APIEndpoint}/dashboard/aiPrompt/copied/${userId}`,

yarn.lock

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12273,4 +12273,4 @@ yocto-queue@^0.1.0:
1227312273
yoctocolors-cjs@^2.1.2:
1227412274
version "2.1.3"
1227512275
resolved "https://registry.npmjs.org/yoctocolors-cjs/-/yoctocolors-cjs-2.1.3.tgz"
12276-
integrity sha512-U/PBtDf35ff0D8X8D0jfdzHYEPFxAI7jJlxZXwCSez5M3190m+QobIfh+sWDWSHMCWWJN2AWamkegn6vr6YBTw==
12276+
integrity sha512-U/PBtDf35ff0D8X8D0jfdzHYEPFxAI7jJlxZXwCSez5M3190m+QobIfh+sWDWSHMCWWJN2AWamkegn6vr6YBTw==

0 commit comments

Comments
 (0)