-
Notifications
You must be signed in to change notification settings - Fork 2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[IDOL, shoutouts] - edit shoutouts #578
base: main
Are you sure you want to change the base?
Changes from all commits
2232e90
f0d4b3b
0b096f3
21e3a25
261dcbf
7684923
f878a1b
b6802ef
e6b16a9
54e82e5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -64,6 +64,18 @@ export const hideShoutout = async ( | |
await shoutoutsDao.updateShoutout({ ...shoutout, hidden: hide }); | ||
}; | ||
|
||
export const editShoutout = async ( | ||
uuid: string, | ||
newMessage: string, | ||
user: IdolMember | ||
): Promise<Shoutout> => { | ||
const shoutout = await shoutoutsDao.getShoutout(uuid); | ||
if (!shoutout) { | ||
throw new NotFoundError(`Shoutout with uuid: ${uuid} does not exist!`); | ||
} | ||
return shoutoutsDao.editShoutout(uuid, newMessage); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. see if you can use |
||
}; | ||
|
||
/** | ||
* Deletes a shoutout, ensuring the user has the necessary permissions or ownership. | ||
* @throws {NotFoundError} If no shoutout with the provided uuid is found. | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -76,6 +76,20 @@ export default class ShoutoutsDao extends BaseDao<Shoutout, DBShoutout> { | |
return this.createDocument(shoutoutWithUUID.uuid, shoutoutWithUUID); | ||
} | ||
|
||
/** | ||
* Edits a shoutout | ||
* @param uuid - uuid of the shoutout | ||
* @param shoutout - shoutout object | ||
*/ | ||
async editShoutout(uuid: string, newMessage: string): Promise<Shoutout> { | ||
await this.collection.doc(uuid).update({ message: newMessage }); | ||
const updatedDoc = await this.getDocument(uuid); | ||
if (!updatedDoc) { | ||
throw new Error('Failed to fetch updated shoutout...'); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remove the EDIT: also see Patricia's comment, we shouldn't be doing permissions checks here There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also, could you use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is more for future reference, but I think checking if the document exists in the DAO layer might be repetitive, since we already check if the uuid exists in the API layer. Not including the checks here also helps keep the DAO files consistent, since most of them do not have additional checks. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. woah thanks for catching this @patriciaahuang, not sure how I didn't see it. We should not do any permissions checks at the DAO layer. That's reserved for the API layer! but since we're likely deleting this function anyways, yeah it's good to know for future reference. |
||
} | ||
return updatedDoc; | ||
} | ||
|
||
/** | ||
* Updates a shoutout | ||
* @param shoutout - shoutout object | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,30 +1,80 @@ | ||
import { Card } from 'semantic-ui-react'; | ||
import React, { useState, Dispatch, SetStateAction } from 'react'; | ||
import { Button, Card, Form, Icon, Modal, TextArea } from 'semantic-ui-react'; | ||
import ShoutoutsAPI from '../../../API/ShoutoutsAPI'; | ||
import ShoutoutDeleteModal from '../../Modals/ShoutoutDeleteModal'; | ||
import styles from './ShoutoutCard.module.css'; | ||
|
||
const ShoutoutCard = (props: { | ||
interface ShoutoutCardProps { | ||
shoutout: Shoutout; | ||
setGivenShoutouts: React.Dispatch<React.SetStateAction<Shoutout[]>>; | ||
}): JSX.Element => { | ||
const { shoutout, setGivenShoutouts } = props; | ||
setGivenShoutouts: Dispatch<SetStateAction<Shoutout[]>>; | ||
} | ||
|
||
const fromString = shoutout.isAnon | ||
? 'From: Anonymous' | ||
: `From: ${shoutout.giver?.firstName} ${shoutout.giver?.lastName}`; | ||
const dateString = `${new Date(shoutout.timestamp).toDateString()}`; | ||
const ShoutoutCard: React.FC<ShoutoutCardProps> = ({ shoutout, setGivenShoutouts }) => { | ||
const [isEditing, setIsEditing] = useState(false); | ||
const [editedMessage, setEditedMessage] = useState(shoutout.message); | ||
|
||
const handleEditShoutout = async () => { | ||
try { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we can do without this |
||
await ShoutoutsAPI.updateShoutout(shoutout.uuid, { message: editedMessage }); | ||
setGivenShoutouts((prevShoutouts) => | ||
prevShoutouts.map((s) => (s.uuid === shoutout.uuid ? { ...s, message: editedMessage } : s)) | ||
); | ||
setIsEditing(false); | ||
} catch (error) { | ||
console.error('Failed to edit shoutout:', error); | ||
} | ||
}; | ||
|
||
return ( | ||
<Card className={styles.shoutoutCardContainer}> | ||
<Card.Group widths="equal" className={styles.shoutoutCardDetails}> | ||
<Card.Content header={`To: ${shoutout.receiver}`} className={styles.shoutoutTo} /> | ||
<Card.Content className={styles.shoutoutDate} content={dateString} /> | ||
</Card.Group> | ||
<Card.Group widths="equal" className={styles.shoutoutDelete}> | ||
<Card.Meta className={styles.shoutoutFrom} content={fromString} /> | ||
<ShoutoutDeleteModal uuid={shoutout.uuid} setGivenShoutouts={setGivenShoutouts} /> | ||
<Card.Content | ||
className={styles.shoutoutDate} | ||
content={new Date(shoutout.timestamp).toDateString()} | ||
/> | ||
</Card.Group> | ||
<div className={styles.shoutoutActions}> | ||
<Card.Meta | ||
className={styles.shoutoutFrom} | ||
content={ | ||
shoutout.isAnon | ||
? 'From: Anonymous' | ||
: `From: ${shoutout.giver.firstName} ${shoutout.giver.lastName}` | ||
} | ||
/> | ||
<div> | ||
<ShoutoutDeleteModal uuid={shoutout.uuid} setGivenShoutouts={setGivenShoutouts} /> | ||
<Button icon onClick={() => setIsEditing(true)}> | ||
<Icon name="edit" /> | ||
</Button> | ||
</div> | ||
</div> | ||
<Card.Content description={shoutout.message} /> | ||
<Modal open={isEditing} onClose={() => setIsEditing(false)}> | ||
<Modal.Header>Edit Shoutout</Modal.Header> | ||
<Modal.Content> | ||
<Form> | ||
<Form.Field> | ||
<label>Message</label> | ||
<TextArea | ||
value={editedMessage} | ||
onChange={(e, { value }) => setEditedMessage((value || '') as string)} | ||
/> | ||
</Form.Field> | ||
</Form> | ||
</Modal.Content> | ||
<Modal.Actions> | ||
<Button onClick={() => setIsEditing(false)} negative> | ||
Cancel | ||
</Button> | ||
<Button onClick={handleEditShoutout} positive> | ||
Save | ||
</Button> | ||
</Modal.Actions> | ||
</Modal> | ||
</Card> | ||
); | ||
}; | ||
|
||
export default ShoutoutCard; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,5 @@ | ||
.trashIcon { | ||
position: absolute; | ||
right: 1%; | ||
margin-top: 2rem; | ||
position: relative; | ||
right: 10%; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you add permissions checking here to make sure that the shoutout belongs to the user who's trying to edit it OR it's a leadOrAdmin?