Custom Button: Back to Mapping
Elisa Dinsmore avatar
Written by Elisa Dinsmore
Updated over a week ago

You may run into a situation where users have mapped their data and realize they accidentally mapped a column to the wrong header, or need to make modifications to their column mapping that they didn't realize on import. While they could start a new session, or delete everything in the table and start over manually, you could also set up a "back to mapping" button for them that will delete the data in the review table for them and get them back in the mapping experience with their file.

The code below will demonstrate how to do this in your listener. This will require a minimum @flatfile/listener version of 0.3.18.

 /**
* When the mapping job completes, let's cache the last config so we can restore it during the back
* operation and let's add the back action.
*/
listener.on("job:completed", { operation: "map" }, async ({ context }) => {
const { data: job } = await api.jobs.get(context.jobId);
// @ts-ignore
const { data: sheet } = await api.sheets.get(job.config.destinationSheetId);
const { data: workbook } = await api.workbooks.get(sheet.workbookId);

await api.workbooks.update(workbook.id, {
metadata: {
lastMappingConfig: {
type: "workbook",
operation: "map",
trigger: "manual",
source: job.source,
destination: job.destination,
mode: "foreground",
config: job.config,
},
},
actions: [
{
operation: "backToMappingAction",
mode: "foreground",
label: "Back to Mapping",
tooltip: "Go back to the mapping screen",
confirm: true,
description:
"Are you sure you want to go back to mapping? Any changes to data will be lost.",
},
// If you already have a submit action, you'll want to include this in the update call, otherwise it will be overwritten
{
operation: "submitAction",
mode: "foreground",
label: "Submit",
description: "Submit data to webhook.site",
primary: true,
},
],
});
});

/**
* Handle the back to mapping job by deleting records. Do not complete the job here, pick it up
* after the data deletion job completes.
*/
listener.on(
"job:ready",
{ operation: "backToMappingAction" },
async ({ context }) => {
const { jobId, workbookId } = context;

await api.jobs.ack(jobId, {
info: "One moment...",
progress: 10,
});

const { data: workbook } = await api.workbooks.get(workbookId);

// remove the back action
await api.workbooks.update(context.workbookId, {
actions: workbook.actions!.filter(
(a) => a.operation !== "backToMappingAction"
),
});

const sheets = await api.sheets.list({ workbookId });

await api.jobs.ack(jobId, {
info: "Deleting records...",
progress: 30,
});

// @ts-ignore
await api.jobs.create({
operation: "delete-records",
type: "workbook",
source: workbookId,
trigger: "immediate",
config: {
sheet: sheets.data[0].id,
filter: "all",
},
input: {
proceedMappingAfterComplete: true,
originalJobId: jobId,
},
});
}
);

/**
* After records are deleted, check to see if it was initiated by a back action. If so
* spin up a new mapping job using the most recent configuration and direct the user.
*/
listener.on(
"job:completed",
{ operation: "delete-records" },
async ({ context }) => {
const { data: job } = await api.jobs.get(context.jobId);
const { data: workbook } = await api.workbooks.get(context.workbookId);

if (
job.input!.proceedMappingAfterComplete &&
workbook.metadata.lastMappingConfig
) {
const { data: mappingJob } = await api.jobs.create(
workbook.metadata.lastMappingConfig
);
await api.jobs.complete(job.input!.originalJobId, {
outcome: {
acknowledge: true,
heading: "Reset complete, ready to redo mapping...",
next: {
type: "id",
id: context.spaceId,
path: `job/${mappingJob.id}`,
label: "Back to Mapping",
},
hideDefaultButton: true,
},
});
}
}
);
Did this answer your question?