Skip to content

Commit 852f56f

Browse files
committed
feat(excel-preview): initial implementation of Excel Preview UI
1 parent fb67de6 commit 852f56f

File tree

3 files changed

+95
-6
lines changed

3 files changed

+95
-6
lines changed

README.md

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ You are welcome to **test, explore, and contribute**! 🚀
1717
✅ Export any Doctype data into Excel with custom formats
1818
✅ Define multiple Excel templates for the same Doctype
1919
✅ Easy-to-use interface to create, manage, and apply Excel formats
20-
Preview feature to test the format before export
20+
🧪 **Excel Preview feature** to test the format before export (**🔬 In Testing Stage**)
2121
**Attach or Download Excel:** Option to attach Excel to the document or directly download it
2222

2323
---
@@ -38,13 +38,15 @@ bench --site your-site-name install-app xcelform
3838
1. Go to the respective Doctype.
3939
2. Click on **XF Excel Format**.
4040
3. Select the desired format.
41-
4. Choose whether to **attach** the file to the document or **download** it.
42-
5. Export the customized Excel file!
41+
4. Click **Preview** to test the format (currently in testing phase).
42+
5. Choose whether to **attach** the file to the document or **download** it.
43+
6. Export the customized Excel file!
4344

4445
---
4546

4647
## ⚡️ **How It Works**
47-
- **Excel Formats:** Define custom Excel formats in the **XF Excel Format** Doctype.
48+
- **Excel Formats:** Define custom Excel formats in the **XF Excel Format** Doctype.
49+
- **Excel Preview(🔬 Testing):** Allows users to preview the Excel layout and formatting before export.
4850
- **Attach or Download:** Users can choose to attach the Excel file to the document or download it.
4951
- **Export Confirmation:** A success message appears when the file is attached or downloaded.
5052

xcelform/public/js/add_custom_button.js

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,35 @@ function show_excel_format_dialog(frm) {
116116
});
117117
}
118118
},
119+
secondary_action_label: __("Preview"),
120+
secondary_action(values) {
121+
console.log("hiii")
122+
frappe.call({
123+
method: "xcelform.xcelform.hooks.xf_excel_utils.export_to_xf_excel",
124+
args: {
125+
doctype: frm.doc.doctype,
126+
doc_name: frm.doc.name,
127+
excel_format: values.excel_format,
128+
is_preview: 1
129+
},
130+
callback: function (r) {
131+
if (r.message) {
132+
let previewHtml = r.message.preview;
133+
let dialog = new frappe.ui.Dialog({
134+
title: "Excel Preview",
135+
fields: [
136+
{
137+
fieldtype: "HTML",
138+
fieldname: "preview",
139+
options: `<div style="overflow:auto; max-height:500px;">${previewHtml}</div>`
140+
}
141+
]
142+
});
143+
dialog.show();
144+
}
145+
}
146+
});
147+
}
119148
});
120149

121150
d.show();

xcelform/xcelform/hooks/xf_excel_utils.py

Lines changed: 60 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,13 @@
44
from openpyxl.utils import get_column_letter
55
from openpyxl.drawing.image import Image
66
from frappe.utils.file_manager import save_file
7-
7+
from openpyxl import load_workbook
8+
import pandas as pd
9+
from io import BytesIO
810

911
@frappe.whitelist()
1012
def export_to_xf_excel(
11-
doctype, doc_name, excel_format=None, attach=False, download=False
13+
doctype, doc_name, excel_format=None, attach=False, download=False,is_preview=False
1214
):
1315

1416
doc = frappe.get_doc(doctype, doc_name)
@@ -59,6 +61,10 @@ def export_to_xf_excel(
5961

6062
file_name = f"XF_Excel_{doctype}_{doc_name}.xlsx"
6163

64+
if is_preview:
65+
html_preview = generate_excel_preview(output)
66+
return {"preview": html_preview}
67+
6268
if attach:
6369
file_doc = save_file(file_name, output.read(), doctype, doc_name, is_private=1)
6470
return file_doc.file_url
@@ -79,3 +85,55 @@ def get_default_excel_format(doctype):
7985
"XF Excel Format", {"doc_type": doctype, "is_default": 1}, "name"
8086
)
8187
return default_format
88+
89+
90+
def rgb_to_hex(rgb):
91+
"""Convert Excel's ARGB color to HEX"""
92+
if rgb is None or rgb == "00000000":
93+
return "#FFFFFF"
94+
return f"#{rgb[-6:]}"
95+
96+
def generate_excel_preview(output):
97+
output.seek(0)
98+
wb = load_workbook(BytesIO(output.read()))
99+
ws = wb.active
100+
101+
merged_cells = {}
102+
for merged_range in ws.merged_cells.ranges:
103+
min_row, min_col, max_row, max_col = merged_range.min_row, merged_range.min_col, merged_range.max_row, merged_range.max_col
104+
merged_cells[(min_row, min_col)] = (max_row - min_row + 1, max_col - min_col + 1)
105+
106+
107+
html = "<table border='1' style='border-collapse: collapse; width: 100%; text-align: center;'>"
108+
109+
for row_idx, row in enumerate(ws.iter_rows(), start=1):
110+
html += "<tr>"
111+
for col_idx, cell in enumerate(row, start=1):
112+
if any((row_idx, col_idx) in merged_range for merged_range in merged_cells if (row_idx, col_idx) != merged_range):
113+
continue
114+
115+
rowspan, colspan = merged_cells.get((row_idx, col_idx), (1, 1))
116+
rowspan_attr = f' rowspan="{rowspan}"' if rowspan > 1 else ""
117+
colspan_attr = f' colspan="{colspan}"' if colspan > 1 else ""
118+
119+
120+
bold = "font-weight: bold;" if cell.font and cell.font.bold else ""
121+
bg_color = rgb_to_hex(cell.fill.start_color.rgb)
122+
text_align = "center"
123+
if cell.alignment:
124+
if cell.alignment.horizontal:
125+
text_align = cell.alignment.horizontal
126+
elif isinstance(cell.alignment, Alignment):
127+
text_align = cell.alignment.horizontal or "center"
128+
129+
130+
value = cell.value if cell.value is not None else ""
131+
132+
cell_style = f"background-color: {bg_color}; text-align: {text_align}; padding: 5px; border: 1px solid black; {bold}".strip()
133+
html += f"<td style=\"{cell_style}\"{rowspan_attr}{colspan_attr}>{value}</td>"
134+
html += "</tr>"
135+
136+
html += "</table>"
137+
138+
return html
139+

0 commit comments

Comments
 (0)