frappe.ui.form.on('Production Briefing Form', { refresh(frm) { if (!frm._image_editor_button_added) { frm.fields_dict.briefing_items.grid.add_custom_button( __('Edit Selected Image'), () => edit_selected_row_image(frm) ); frm._image_editor_button_added = true; } } }); /* -------------------------------------------------- */ /* GLOBAL MESSAGE LISTENER (REGISTER ONCE) */ /* -------------------------------------------------- */ if (!window._pbf_image_editor_listener) { window.addEventListener('message', function (event) { if (event.origin !== window.location.origin) return; if (event.data?.type === 'image_edited') { handleEditedImage(cur_frm, event.data); } }); window._pbf_image_editor_listener = true; } /* -------------------------------------------------- */ /* EDIT BUTTON HANDLER */ /* -------------------------------------------------- */ function edit_selected_row_image(frm) { // -------------------------------------------------- // PROJECT RESTRICTION // -------------------------------------------------- if (frm.doc.project !== "DD-PROJ-2158") { frappe.msgprint(__('Image editor is allowed only for Project: DD-PROJ-2158')); return; } const grid = frm.fields_dict.briefing_items.grid; const selected = grid.get_selected_children(); if (!selected.length) { frappe.msgprint(__('Please select a row.')); return; } if (selected.length > 1) { frappe.msgprint(__('Please select only one row.')); return; } const row = selected[0]; if (!row.product_design) { frappe.msgprint(__('No image attached in this row.')); return; } if (!row.item_code) { frappe.msgprint(__('No Item selected in this row.')); return; } // -------------------------------------------------- // ITEM GROUP CHECK // -------------------------------------------------- frappe.db.get_value('Item', row.item_code, 'item_group').then(r => { if (!r.message) return; if (r.message.item_group !== "Flush Door") { frappe.msgprint(__('Image editor is allowed only for Item Group: Flush Door')); return; } // All validations passed → Open editor openImageEditor(row); }); } /* -------------------------------------------------- */ /* OPEN IMAGE EDITOR */ /* -------------------------------------------------- */ function openImageEditor(row) { const file_url = row.attach; let editor_image_url; if (file_url.startsWith('/private/')) { editor_image_url = `/api/method/frappe.utils.file_manager.download_file` + `?file_url=${encodeURIComponent(file_url)}`; } else { editor_image_url = file_url.startsWith('http') ? file_url : window.location.origin + file_url; } const params = new URLSearchParams({ image_url: editor_image_url, row_name: row.name }); window.open( `/image-editor1?${params.toString()}`, 'pbf', 'width=1400,height=900,resizable=yes,scrollbars=yes' ); } /* -------------------------------------------------- */ /* HANDLE EDITED IMAGE */ /* -------------------------------------------------- */ function handleEditedImage(frm, data) { if (!data.imageData || !data.metadata?.row_name) return; fetch(data.imageData) .then(res => res.blob()) .then(blob => { const file = new File( [blob], `edited_${Date.now()}.png`, { type: 'image/png' } ); const formData = new FormData(); formData.append('file', file); formData.append('is_private', 0); formData.append('doctype', frm.doctype); formData.append('docname', frm.docname); formData.append('fieldname', 'attach'); return fetch('/api/method/upload_file', { method: 'POST', body: formData, headers: { 'X-Frappe-CSRF-Token': frappe.csrf_token } }); }) .then(res => res.json()) .then(r => { if (!r.message?.file_url) return; const row = frm.doc.briefing_items.find( d => d.name === data.metadata.row_name ); if (!row) return; frappe.model.set_value( row.doctype, row.name, 'attach', r.message.file_url ); frm.refresh_field('briefing_items'); frappe.show_alert({ message: __('Image updated successfully'), indicator: 'green' }); }) .catch(err => { console.error(err); frappe.msgprint(__('Failed to save edited image.')); }); }