feat: let's copy data to clipboard from listview and reportview

This commit is contained in:
git-avc 2025-11-22 21:11:48 +01:00
parent 370efbd3e8
commit c0606abfd3

View file

@ -2296,6 +2296,170 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList {
};
};
const copy_to_clipboard = () => {
return {
label: __("Copy to Clipboard"),
action: () => {
const selected_items = this.get_checked_items();
if (selected_items.length === 0) {
frappe.show_alert({
message: __("No rows selected"),
indicator: "orange",
});
return;
}
let columns;
if (
this.columns &&
this.columns.length &&
(this.columns[0].docfield || this.columns[0].type == "Status")
) {
// Report View - has columns with docfield property
columns = this.columns.map((col) => ({
fieldname: col.id || col.field,
label: col.content || col.name,
docfield: col.docfield, // Keep the docfield for link field processing
}));
} else if (this.columns && this.columns.length && this.columns[0].type) {
// List View - has columns with type and df properties
console.log(this.columns);
columns = this.columns
.filter((col) => {
// Include columns with df.fieldname, or Subject/Status types
return (
(col.df && col.df.fieldname) ||
col.type === "Subject" ||
col.type === "Status"
);
})
.map((col) => {
if (col.type === "Subject") {
return {
fieldname: col.df?.fieldname || "name",
label: __(col.df?.label || "ID"),
type: "Subject",
};
} else if (col.type === "Status") {
return {
fieldname: "status",
label: __("Status"),
type: "Status",
};
} else {
return {
fieldname: col.df.fieldname,
label: __(col.df.label || col.df.fieldname),
type: col.type,
};
}
});
}
// Prepare data for clipboard
const headers = columns.map((col) => col.label).join("\t");
const rows = selected_items.map((item) => {
return columns
.map((col) => {
console.log("Processing column:", col.fieldname, "col:", col);
let value;
const df = col.df || col.docfield;
// Check if this is a Status column (by type) or docstatus field (in Report view)
if (col.type === "Status" || col.fieldname === "docstatus") {
// For Status columns, get the indicator text
const indicator = frappe.get_indicator(item, this.doctype);
if (indicator && indicator.length > 0) {
value = indicator[0];
} else {
// Fallback to status field
value = item.status || "";
}
} else {
// Check if this is a link field with title field
const link_title_fieldname =
this.link_field_title_fields?.[col.fieldname];
if (link_title_fieldname) {
// List view: Use the title field value if available
value =
item[col.fieldname + "_" + link_title_fieldname] ||
item[col.fieldname];
console.log(
"List view link:",
col.fieldname,
"title_field:",
link_title_fieldname,
"value:",
value
);
} else if (
df &&
df.fieldtype === "Link" &&
df.options &&
item[col.fieldname]
) {
// For Link fields, try to get the title
console.log(
"Processing link field:",
col.fieldname,
"doctype:",
df.options,
"item value:",
item[col.fieldname],
"item:",
item
);
// First check if the doctype has show_title_field_in_link enabled
if (
frappe.boot.link_title_doctypes?.includes(df.options)
) {
// Try to get from cache
let link_title = frappe.utils.get_link_title(
df.options,
item[col.fieldname]
);
console.log(
"Link field:",
col.fieldname,
"doctype:",
df.options,
"value:",
item[col.fieldname],
"cached title:",
link_title,
"cache key:",
df.options + "::" + item[col.fieldname]
);
value = link_title || item[col.fieldname];
} else {
value = item[col.fieldname];
}
} else {
value = item[col.fieldname];
}
}
// Handle null/undefined values
if (value == null) return "";
// Convert to string and remove HTML tags if any
return String(value).replace(/<[^>]*>/g, "");
})
.join("\t");
});
const clipboard_data = [headers, ...rows].join("\n"); // Copy to clipboard
frappe.utils.copy_to_clipboard(clipboard_data).then(() => {
frappe.show_alert({
message: __("{0} row(s) copied to clipboard", [selected_items.length]),
indicator: "green",
});
});
},
standard: true,
};
};
// Copy to clipboard
actions_menu_items.push(copy_to_clipboard());
// bulk edit
if (has_editable_fields(doctype) && is_bulk_edit_allowed(doctype)) {
actions_menu_items.push(bulk_edit());