Skip to content

Commit cb8f20e

Browse files
committed
E-7-Admin-Edit-Save
1 parent 9008e51 commit cb8f20e

File tree

2 files changed

+213
-40
lines changed

2 files changed

+213
-40
lines changed

code/frontend/src/pages/Home.jsx

Lines changed: 209 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
import { useAuth } from "../contexts/AuthContext";
22
import { useState, useEffect } from "react";
33
import { useNavigate } from "react-router-dom";
4-
import { Tabs, Tab, Snackbar, Alert, IconButton, Button } from "@mui/material";
4+
import { Tabs, Tab, Snackbar, Alert, IconButton, Button, TextField } from "@mui/material";
55
import { styled } from "@mui/material/styles";
66
import LogoutIcon from "@mui/icons-material/Logout";
7+
import EditIcon from "@mui/icons-material/Edit";
8+
import SaveIcon from "@mui/icons-material/Save";
9+
import CancelIcon from "@mui/icons-material/Cancel";
710
import { activityService } from "../services/activityService";
811
import AvatarUpload from "../components/Avator";
912
import ParticipantsList from "../components/ParticipantsList";
@@ -45,6 +48,8 @@ export default function Home() {
4548
severity: "success",
4649
actions: null,
4750
});
51+
const [editingActivity, setEditingActivity] = useState(null);
52+
const [editedActivity, setEditedActivity] = useState(null);
4853

4954
const handleChange = async (event, newValue) => {
5055
setValue(newValue);
@@ -256,6 +261,84 @@ export default function Home() {
256261
navigate("/create-activity");
257262
};
258263

264+
const handleEditActivity = (activity) => {
265+
setEditingActivity(activity.activityId);
266+
267+
// Convert backend date format (YYYY-MM-DD HH:mm) to datetime-local format (YYYY-MM-DDTHH:mm)
268+
const convertToDateTimeLocal = (dateTimeStr) => {
269+
if (!dateTimeStr) return '';
270+
// Replace space with T for datetime-local input
271+
return dateTimeStr.replace(' ', 'T');
272+
};
273+
274+
setEditedActivity({
275+
name: activity.name,
276+
description: activity.description,
277+
location: activity.location,
278+
startDateTime: convertToDateTimeLocal(activity.startDateTime),
279+
endDateTime: convertToDateTimeLocal(activity.endDateTime)
280+
});
281+
};
282+
283+
const handleCancelEdit = () => {
284+
setEditingActivity(null);
285+
setEditedActivity(null);
286+
};
287+
288+
const handleSaveEdit = async (activityId) => {
289+
try {
290+
// Format the dates to match the backend's expected format (YYYY-MM-DD HH:mm)
291+
const formatDateTime = (dateTimeStr) => {
292+
if (!dateTimeStr) return '';
293+
294+
// Parse the datetime-local input value and format it properly
295+
const date = new Date(dateTimeStr);
296+
297+
// Format as YYYY-MM-DD HH:mm (without seconds)
298+
const year = date.getFullYear();
299+
const month = String(date.getMonth() + 1).padStart(2, '0');
300+
const day = String(date.getDate()).padStart(2, '0');
301+
const hours = String(date.getHours()).padStart(2, '0');
302+
const minutes = String(date.getMinutes()).padStart(2, '0');
303+
304+
return `${year}-${month}-${day} ${hours}:${minutes}`;
305+
};
306+
307+
const formattedActivity = {
308+
name: editedActivity.name || '',
309+
description: editedActivity.description || '',
310+
location: editedActivity.location || '',
311+
startDateTime: formatDateTime(editedActivity.startDateTime),
312+
endDateTime: formatDateTime(editedActivity.endDateTime)
313+
};
314+
315+
console.log("Payload to updateActivity:", formattedActivity);
316+
317+
await activityService.updateActivity(activityId, formattedActivity);
318+
const activities = await activityService.getParticipantActivities();
319+
setParticipantActivities(activities);
320+
setEditingActivity(null);
321+
setEditedActivity(null);
322+
notifySuccess("Activity updated successfully");
323+
} catch (error) {
324+
console.error("Error updating activity:", error);
325+
let errorMessage = "Failed to update activity";
326+
if (error.response?.data?.message) {
327+
errorMessage = error.response.data.message;
328+
} else if (error.response?.data?.errors) {
329+
errorMessage = JSON.stringify(error.response.data.errors);
330+
}
331+
notifyError(errorMessage);
332+
}
333+
};
334+
335+
const handleEditChange = (field, value) => {
336+
setEditedActivity(prev => ({
337+
...prev,
338+
[field]: value
339+
}));
340+
};
341+
259342
return (
260343
<div style={styles.container}>
261344
<Snackbar
@@ -360,53 +443,130 @@ export default function Home() {
360443
<TabPanel value={value} index={1}>
361444
<div style={styles.activitiesGrid}>
362445
{participantActivities.map((activity, index) => {
363-
console.log("participantActivities", activity);
364446
const isAdmin = activity.role === "ADMIN";
447+
const isEditing = editingActivity === activity.activityId;
365448
const startDate = new Date(activity.startDateTime);
366449
const endDate = new Date(activity.endDateTime);
367450
const formatDateTime = (date) =>
368451
`${date.toLocaleDateString()} ${date.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" })}`;
369452

370453
return (
371454
<div key={index} style={styles.activityCard}>
372-
<h3 style={styles.activityTitle}>{activity.name}</h3>
373-
<p style={styles.activityDescription}>{activity.description}</p>
374-
<div style={styles.activityDetails}>
375-
<div style={styles.activityMeta}>
376-
<span style={styles.metaIcon}>🕐</span>
377-
<span>
378-
<strong>Start:</strong> {formatDateTime(startDate)}
379-
</span>
380-
</div>
381-
<div style={styles.activityMeta}>
382-
<span style={styles.metaIcon}>🕕</span>
383-
<span>
384-
<strong>End:</strong> {formatDateTime(endDate)}
385-
</span>
386-
</div>
387-
<div style={styles.activityMeta}>
388-
<span style={styles.metaIcon}>📍</span>
389-
<span>{activity.location}</span>
390-
</div>
391-
</div>
392-
<div style={styles.activityActions}>
393-
<ParticipantsList
394-
activityId={activity.activityId}
395-
onError={(message) => setNotification({
396-
open: true,
397-
message,
398-
severity: "error",
399-
})}
400-
/>
401-
<button
402-
style={styles.joinButton}
403-
onClick={() =>
404-
isAdmin ? handleDeleteActivity(activity.activityId) : handleLeaveActivity(activity.activityId)
405-
}
406-
>
407-
{isAdmin ? "Delete Activity" : "Leave Activity"}
408-
</button>
409-
</div>
455+
{isEditing ? (
456+
<>
457+
<TextField
458+
fullWidth
459+
value={editedActivity.name}
460+
onChange={(e) => handleEditChange("name", e.target.value)}
461+
margin="normal"
462+
label="Activity Name"
463+
/>
464+
<TextField
465+
fullWidth
466+
multiline
467+
rows={3}
468+
value={editedActivity.description}
469+
onChange={(e) => handleEditChange("description", e.target.value)}
470+
margin="normal"
471+
label="Description"
472+
/>
473+
<TextField
474+
fullWidth
475+
value={editedActivity.location}
476+
onChange={(e) => handleEditChange("location", e.target.value)}
477+
margin="normal"
478+
label="Location"
479+
/>
480+
<TextField
481+
fullWidth
482+
type="datetime-local"
483+
value={editedActivity.startDateTime.slice(0, 16)}
484+
onChange={(e) => handleEditChange("startDateTime", e.target.value)}
485+
margin="normal"
486+
label="Start Time"
487+
InputLabelProps={{ shrink: true }}
488+
/>
489+
<TextField
490+
fullWidth
491+
type="datetime-local"
492+
value={editedActivity.endDateTime.slice(0, 16)}
493+
onChange={(e) => handleEditChange("endDateTime", e.target.value)}
494+
margin="normal"
495+
label="End Time"
496+
InputLabelProps={{ shrink: true }}
497+
/>
498+
<div style={styles.editActions}>
499+
<Button
500+
variant="contained"
501+
color="primary"
502+
startIcon={<SaveIcon />}
503+
onClick={() => handleSaveEdit(activity.activityId)}
504+
>
505+
Save
506+
</Button>
507+
<Button
508+
variant="outlined"
509+
color="secondary"
510+
startIcon={<CancelIcon />}
511+
onClick={handleCancelEdit}
512+
>
513+
Cancel
514+
</Button>
515+
</div>
516+
</>
517+
) : (
518+
<>
519+
<h3 style={styles.activityTitle}>{activity.name}</h3>
520+
<p style={styles.activityDescription}>{activity.description}</p>
521+
<div style={styles.activityDetails}>
522+
<div style={styles.activityMeta}>
523+
<span style={styles.metaIcon}>🕐</span>
524+
<span>
525+
<strong>Start:</strong> {formatDateTime(startDate)}
526+
</span>
527+
</div>
528+
<div style={styles.activityMeta}>
529+
<span style={styles.metaIcon}>🕕</span>
530+
<span>
531+
<strong>End:</strong> {formatDateTime(endDate)}
532+
</span>
533+
</div>
534+
<div style={styles.activityMeta}>
535+
<span style={styles.metaIcon}>📍</span>
536+
<span>{activity.location}</span>
537+
</div>
538+
</div>
539+
<div style={styles.activityActions}>
540+
<ParticipantsList
541+
activityId={activity.activityId}
542+
onError={(message) => setNotification({
543+
open: true,
544+
message,
545+
severity: "error",
546+
})}
547+
/>
548+
{isAdmin && (
549+
<Button
550+
variant="outlined"
551+
color="primary"
552+
startIcon={<EditIcon />}
553+
onClick={() => handleEditActivity(activity)}
554+
style={styles.editButton}
555+
>
556+
Edit Activity
557+
</Button>
558+
)}
559+
<button
560+
style={styles.joinButton}
561+
onClick={() =>
562+
isAdmin ? handleDeleteActivity(activity.activityId) : handleLeaveActivity(activity.activityId)
563+
}
564+
>
565+
{isAdmin ? "Delete Activity" : "Leave Activity"}
566+
</button>
567+
</div>
568+
</>
569+
)}
410570
</div>
411571
);
412572
})}
@@ -678,4 +838,13 @@ const styles = {
678838
margin: "0 auto",
679839
boxSizing: "border-box",
680840
},
841+
editActions: {
842+
display: "flex",
843+
gap: "1rem",
844+
marginTop: "1rem",
845+
justifyContent: "center",
846+
},
847+
editButton: {
848+
marginBottom: "0.5rem",
849+
},
681850
};

code/frontend/src/services/activityService.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,4 +58,8 @@ export const activityService = {
5858
});
5959
return response.data.data;
6060
},
61+
updateActivity: async (activityId, activityData) => {
62+
const response = await api.put(`/activity/${activityId}`, activityData);
63+
return response.data;
64+
},
6165
};

0 commit comments

Comments
 (0)