To put you in context here's the vehicle view on the Fuel Tracker App.
First of all, and for my own projects (this one is fairly new), I'm trying to get the habit of creating new branches feature-specific so I focus in one thing that is the feature and I don't make small changes when I see them (I write them down and fix them later).
git checkout -b feature/edit-refuel-from-table
Next up, adding the ui. This will be pretty straight-forward because we just need a new column and an icon which will allow us to edit the refuel.
I thought of making a modal for it but was too much. Editing on the same line is much faster and more comfy.
Add column
Let's add the column to the headers:
const tableHeaders = [
{ label: user.distanceUnit, key: "distance" },
{ label: "Price", key: "price" },
{ label: `Paid (${currencySymbol(user.currency)})`, key: "paid" },
{ label: "Created", key: "createdAt" },
{ label: "", key: "actions" }, // ⬅️ New column header
];
Now that we have the column, we have to start thinking about editing and saving the row (and showing the icons to do so).
Variables
We'll need to know which row we're in when we edit it (selectedRefuelId
) and we'll need something to store the edited values (editedValues
). We use Partial for the edition because we won't allow the user to change the date where the refuel happened (don't count the examples in this post, they were made just to show what's explained 🤪).
const [selectedRefuelId, setSelectedRefuelId] = useState<number | null>(null);
const [editedValues, setEditedValues] = useState<Partial<Refuel>>({});
Editing and saving functions
handleEdit
: this function will allow us to change the values. It will be fired when the pencil icon is clicked.const handleEdit = (id: number) => { setSelectedRefuelId(id); const refuelToEdit = refuels.find((refuel) => refuel.id === id); setEditedValues(refuelToEdit || {}); };
handleChange
: this function will apply the modifications we make to the values.const handleChange = (key: string, value: string | number) => { setEditedValues((prev) => ({ ...prev, [key]: value })); };
handleCancel
: self explanatory.const handleCancel = () => { setSelectedRefuelId(null); setEditedValues({}); };
handleSave
: with this one we'll send the edited refuel to the backend and fire a notification when everything is okay. For now, we'll log an error if something goes wrong (I'll surely add another notification for the error, not needed now). At the end we'll reset the values so we can continue editing other rows if we want.const handleSave = async (id: number) => { try { await updateRefuel({ id: id, data: editedValues }); } catch (error) { console.error("Error adding fuel:", error); } finally { notification({ type: "success", message: "Refuel updated successfully!", icon: <Check size={20} />, }); setSelectedRefuelId(null); setEditedValues({}); } };
Allowing row edit
For every row, we're gonna play with selectedRefuelId
and the actual refuel.id
of the row to show the input mode or just the number.
<Table.Td w={150} style={{ textAlign: "center" }}>
{selectedRefuelId === refuel.id ? (
<NumberInput
value={editedValues.paid?.toString() || ""}
onChange={(value) => handleChange("paid", value)}
hideControls
/>
) : (
<NumberFormatter
thousandSeparator="."
decimalSeparator=","
decimalScale={2}
value={refuel.paid?.toFixed(2)}
/>
)}
</Table.Td>
Adding the icons + actions in the table
Finally, we'll add the icons to edit, apply edit or cancel. We are using Phosphor Icons.
Instead of using action icons alone, sometimes looks good to have them together so it looks like one button (in this case like a cool pill). We'll group them.
<Table.Td style={{ textAlign: "center" }}>
{selectedRefuelId === refuel.id ? (
<ActionIconGroup flex="row">
<ActionIcon
onClick={() => handleSave(refuel.id)}
color="green"
>
<Check size="1.125rem" />
</ActionIcon>
<ActionIcon onClick={handleCancel} color="red">
<X size="1.125rem" />
</ActionIcon>
</ActionIconGroup>
) : (
<ActionIcon
onClick={() => handleEdit(refuel.id)}
variant="transparent"
color="gray"
>
<Pencil size="1.125rem" />
</ActionIcon>
)}
</Table.Td>
Here's the first row in edit mode (the rest will have the pencil).
After a few tests it worked so the feature is now ready to merge to the main branch and deploy to production 🚀🚀
I'll try to make more posts like this one so I keep you up to date on this and future projects.
Salut!