Skip to content

Commit 3fe27e3

Browse files
committed
feat: implemented edit action
1 parent 862dd2c commit 3fe27e3

File tree

4 files changed

+227
-74
lines changed

4 files changed

+227
-74
lines changed

data/preStatements.json

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,14 @@
77
"actions": [
88
{
99
"id": "a1",
10-
"creationDate": "2024-02-15T10:00:00.000Z",
11-
"dueDate": "2024-02-20T10:00:00.000Z",
10+
"creationDate": "2024-02-15",
11+
"dueDate": "2024-02-20",
1212
"text": "Schedule team meeting"
1313
},
1414
{
1515
"id": "a2",
16-
"creationDate": "2024-02-15T10:00:00.000Z",
17-
"dueDate": "2024-02-22T10:00:00.000Z",
16+
"creationDate": "2024-02-15",
17+
"dueDate": "2024-02-22",
1818
"text": "Send follow-up email"
1919
}
2020
]
@@ -28,8 +28,8 @@
2828
"actions": [
2929
{
3030
"id": "a3",
31-
"creationDate": "2024-02-16T10:00:00.000Z",
32-
"dueDate": "2024-02-25T10:00:00.000Z",
31+
"creationDate": "2024-02-16",
32+
"dueDate": "2024-02-25",
3333
"text": "Review coding guidelines"
3434
}
3535
]
@@ -49,8 +49,8 @@
4949
"actions": [
5050
{
5151
"id": "a4",
52-
"creationDate": "2024-02-17T10:00:00.000Z",
53-
"dueDate": "2024-02-28T10:00:00.000Z",
52+
"creationDate": "2024-02-17",
53+
"dueDate": "2024-02-28",
5454
"text": "Draft social media campaign"
5555
}
5656
]

src/components/statements/ActionPreview.tsx

Lines changed: 188 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import React from 'react';
2-
import { format } from 'date-fns';
3-
import { MoreVertical, Edit2, Trash2, Plus } from 'lucide-react';
2+
// import { format } from 'date-fns';
3+
import { MoreVertical, Edit2, Trash2, Save, X } from 'lucide-react';
44
import { Button } from '../ui/button';
55
import { Input } from '../ui/input';
66
import {
@@ -10,10 +10,14 @@ import {
1010
DropdownMenuItem,
1111
} from '../ui/dropdown-menu';
1212
import type { Action } from '../../../types/types';
13+
import { format, parse } from 'date-fns';
1314

1415
export interface ActionPreviewProps {
1516
actions: Action[];
16-
onEditAction: (actionId: string) => void;
17+
onEditAction: (
18+
actionId: string,
19+
updated: { text: string; dueDate: string }
20+
) => void;
1721
onDeleteAction: (actionId: string) => void;
1822
onAddAction: (newAction: { text: string; dueDate: string }) => void;
1923
}
@@ -24,74 +28,194 @@ const ActionPreview: React.FC<ActionPreviewProps> = ({
2428
onDeleteAction,
2529
onAddAction,
2630
}) => {
27-
const [newAction, setNewAction] = React.useState<{
28-
text: string;
29-
dueDate: string;
30-
}>({
31-
text: '',
32-
dueDate: '',
33-
});
31+
// Add Action State
32+
const [isAddingNew, setIsAddingNew] = React.useState(false);
33+
const [newAction, setNewAction] = React.useState({ text: '', dueDate: '' });
3434

35-
const handleAddAction = () => {
36-
if (newAction.text && newAction.dueDate) {
37-
onAddAction(newAction);
38-
setNewAction({ text: '', dueDate: '' });
39-
}
35+
// Edit Action State
36+
const [editingActionId, setEditingActionId] = React.useState<string | null>(
37+
null
38+
);
39+
const [editForm, setEditForm] = React.useState({ text: '', dueDate: '' });
40+
41+
// ---------------------------
42+
// ADD ACTION LOGIC
43+
// ---------------------------
44+
const handleStartAdd = () => {
45+
setIsAddingNew(true);
46+
setNewAction({ text: '', dueDate: '' });
47+
};
48+
49+
const handleSaveNew = () => {
50+
if (!newAction.text || !newAction.dueDate) return;
51+
onAddAction(newAction);
52+
setIsAddingNew(false);
53+
setNewAction({ text: '', dueDate: '' });
54+
};
55+
56+
const handleCancelNew = () => {
57+
setIsAddingNew(false);
58+
setNewAction({ text: '', dueDate: '' });
59+
};
60+
61+
// ---------------------------
62+
// EDIT ACTION LOGIC
63+
// ---------------------------
64+
const handleStartEdit = (action: Action) => {
65+
setEditingActionId(action.id);
66+
// Directly use the stored dueDate, which is assumed to be in "YYYY-MM-DD" format.
67+
setEditForm({
68+
text: action.text,
69+
dueDate: action.dueDate,
70+
});
71+
};
72+
73+
const handleSaveEdit = (actionId: string) => {
74+
if (!editForm.text || !editForm.dueDate) return;
75+
onEditAction(actionId, { text: editForm.text, dueDate: editForm.dueDate });
76+
setEditingActionId(null);
77+
setEditForm({ text: '', dueDate: '' });
78+
};
79+
80+
const handleCancelEdit = () => {
81+
setEditingActionId(null);
82+
setEditForm({ text: '', dueDate: '' });
4083
};
4184

4285
return (
43-
<div className='space-y-2 mt-4'>
44-
{actions.map((action) => (
45-
<div
46-
key={action.id}
47-
className='bg-gray-50 p-3 rounded-lg flex items-center justify-between'
48-
>
49-
<div>
50-
<p>{action.text}</p>
51-
<p className='text-sm text-gray-500 mt-1'>
52-
Due: {format(new Date(action.dueDate), 'PP')}
53-
</p>
54-
</div>
55-
<DropdownMenu>
56-
<DropdownMenuTrigger asChild>
57-
<Button variant='ghost' size='icon'>
58-
<MoreVertical size={18} className='text-gray-500' />
86+
<div className='space-y-2'>
87+
{actions.map((action) => {
88+
const isEditing = editingActionId === action.id;
89+
90+
if (!isEditing) {
91+
// Normal (read-only) view
92+
// Since dueDate is already in "YYYY-MM-DD", we can display it directly or format it if needed.
93+
return (
94+
<div
95+
key={action.id}
96+
className='flex items-center justify-between bg-gray-50 p-2 rounded'
97+
>
98+
<span className='flex-1'>{action.text}</span>
99+
<span className='mx-4 text-sm text-gray-500'>
100+
Due:{' '}
101+
{action.dueDate
102+
? format(
103+
parse(action.dueDate, 'yyyy-MM-dd', new Date()),
104+
'dd/MM/yyyy'
105+
)
106+
: 'No due date'}
107+
</span>
108+
109+
<DropdownMenu>
110+
<DropdownMenuTrigger asChild>
111+
<button onClick={(e) => e.stopPropagation()}>
112+
<MoreVertical size={18} className='text-gray-500' />
113+
</button>
114+
</DropdownMenuTrigger>
115+
<DropdownMenuContent align='end'>
116+
<DropdownMenuItem onClick={() => handleStartEdit(action)}>
117+
<Edit2 className='mr-2 h-4 w-4' />
118+
Edit
119+
</DropdownMenuItem>
120+
<DropdownMenuItem
121+
onClick={() => onDeleteAction(action.id)}
122+
className='text-red-600'
123+
>
124+
<Trash2 className='mr-2 h-4 w-4' />
125+
Delete
126+
</DropdownMenuItem>
127+
</DropdownMenuContent>
128+
</DropdownMenu>
129+
</div>
130+
);
131+
} else {
132+
// Editing this action
133+
return (
134+
<div
135+
key={action.id}
136+
className='flex items-center bg-gray-50 p-2 rounded space-x-2'
137+
>
138+
<Input
139+
placeholder='Action text'
140+
value={editForm.text}
141+
onChange={(e) =>
142+
setEditForm({ ...editForm, text: e.target.value })
143+
}
144+
className='flex-1'
145+
/>
146+
<Input
147+
type='date'
148+
value={editForm.dueDate}
149+
onChange={(e) =>
150+
setEditForm({ ...editForm, dueDate: e.target.value })
151+
}
152+
className='w-36'
153+
/>
154+
<Button
155+
variant='ghost'
156+
size='sm'
157+
onClick={() => handleSaveEdit(action.id)}
158+
className='text-green-500 hover:text-green-700'
159+
>
160+
<Save size={16} />
59161
</Button>
60-
</DropdownMenuTrigger>
61-
<DropdownMenuContent align='end'>
62-
<DropdownMenuItem onClick={() => onEditAction(action.id)}>
63-
<Edit2 className='mr-2 h-4 w-4' />
64-
Edit
65-
</DropdownMenuItem>
66-
<DropdownMenuItem
67-
onClick={() => onDeleteAction(action.id)}
68-
className='text-red-600'
162+
<Button
163+
variant='ghost'
164+
size='sm'
165+
onClick={handleCancelEdit}
166+
className='text-gray-500 hover:text-gray-700'
69167
>
70-
<Trash2 className='mr-2 h-4 w-4' />
71-
Delete
72-
</DropdownMenuItem>
73-
</DropdownMenuContent>
74-
</DropdownMenu>
168+
<X size={16} />
169+
</Button>
170+
</div>
171+
);
172+
}
173+
})}
174+
175+
{/* Add Action row or inline form */}
176+
{!isAddingNew ? (
177+
<div
178+
className='flex items-center justify-between bg-gray-50 p-2 rounded cursor-pointer hover:bg-gray-100'
179+
onClick={handleStartAdd}
180+
>
181+
<span className='flex-1'>+ Add Action</span>
182+
</div>
183+
) : (
184+
<div className='flex items-center bg-gray-50 p-2 rounded space-x-2'>
185+
<Input
186+
placeholder='Action text'
187+
value={newAction.text}
188+
onChange={(e) =>
189+
setNewAction({ ...newAction, text: e.target.value })
190+
}
191+
className='flex-1'
192+
/>
193+
<Input
194+
type='date'
195+
value={newAction.dueDate}
196+
onChange={(e) =>
197+
setNewAction({ ...newAction, dueDate: e.target.value })
198+
}
199+
className='w-36'
200+
/>
201+
<Button
202+
variant='ghost'
203+
size='sm'
204+
onClick={handleSaveNew}
205+
className='text-green-500 hover:text-green-700'
206+
>
207+
<Save size={16} />
208+
</Button>
209+
<Button
210+
variant='ghost'
211+
size='sm'
212+
onClick={handleCancelNew}
213+
className='text-gray-500 hover:text-gray-700'
214+
>
215+
<X size={16} />
216+
</Button>
75217
</div>
76-
))}
77-
<div className='space-y-2 pt-4 border-t'>
78-
<Input
79-
placeholder='New action'
80-
value={newAction.text}
81-
onChange={(e) => setNewAction({ ...newAction, text: e.target.value })}
82-
/>
83-
<Input
84-
type='date'
85-
value={newAction.dueDate}
86-
onChange={(e) =>
87-
setNewAction({ ...newAction, dueDate: e.target.value })
88-
}
89-
/>
90-
<Button onClick={handleAddAction} className='w-full'>
91-
<Plus className='w-4 h-4 mr-2' />
92-
Add Action
93-
</Button>
94-
</div>
218+
)}
95219
</div>
96220
);
97221
};

src/components/statements/StatementItem.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,12 @@ export interface StatementItemProps {
3131
onDelete: (statementId: string) => void;
3232
onTogglePublic: (statementId: string) => void;
3333
onEditClick: (statementId: string) => void;
34-
// Optional callbacks for action preview functionality
35-
onEditAction?: (actionId: string) => void;
34+
35+
// callbacks for action preview functionality
36+
onEditAction?: (
37+
actionId: string,
38+
updated: { text: string; dueDate: string }
39+
) => void;
3640
onDeleteAction?: (actionId: string) => void;
3741
// Optional callback: receives the statement id and new action details.
3842
onAddAction?: (

src/components/statements/StatementList.tsx

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,30 @@ const StatementList: React.FC<{ username: string }> = ({ username }) => {
157157
updateStatement(updatedStatement);
158158
};
159159

160+
// New: Handler for editing an existing action.
161+
const handleEditAction = (
162+
actionId: string,
163+
updated: { text: string; dueDate: string }
164+
) => {
165+
// Find the statement that contains this action.
166+
const statementToUpdate = statements.find(
167+
(s) => s.actions && s.actions.some((a) => a.id === actionId)
168+
);
169+
if (!statementToUpdate) return;
170+
171+
const updatedActions = statementToUpdate.actions!.map((action) =>
172+
action.id === actionId ? { ...action, ...updated } : action
173+
);
174+
175+
const updatedStatement: Statement = {
176+
...statementToUpdate,
177+
actions: updatedActions,
178+
};
179+
180+
dispatch({ type: 'UPDATE_STATEMENT', payload: updatedStatement });
181+
updateStatement(updatedStatement);
182+
};
183+
160184
return (
161185
<div className='mt-8 bg-white rounded-xl shadow-lg p-6 w-full'>
162186
<h2 className='text-xl font-semibold mb-4'>Created Statements</h2>
@@ -176,6 +200,7 @@ const StatementList: React.FC<{ username: string }> = ({ username }) => {
176200
onTogglePublic={handleTogglePublic}
177201
onEditClick={handleEditClick}
178202
onAddAction={handleAddAction}
203+
onEditAction={handleEditAction}
179204
/>
180205
</li>
181206
))}

0 commit comments

Comments
 (0)