Spaces:
Sleeping
Sleeping
| import gradio as gr | |
| import random | |
| import string | |
| import datetime | |
| from reportlab.lib.pagesizes import A4 | |
| from reportlab.lib import colors | |
| from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle | |
| from reportlab.lib.styles import ParagraphStyle, getSampleStyleSheet | |
| from reportlab.lib.units import cm | |
| # Daftar produk dan kategori | |
| categories = { | |
| "Kategori A": ["Produk A1", "Produk A2", "Produk A3", "Produk A4", "Produk A5", "Produk A6"], | |
| "Kategori B": ["Produk B1", "Produk B2", "Produk B3", "Produk B4", "Produk B5", "Produk B6"], | |
| "Kategori C": ["Produk C1", "Produk C2", "Produk C3", "Produk C4", "Produk C5", "Produk C6"], | |
| "Kategori D": ["Produk D1", "Produk D2", "Produk D3", "Produk D4", "Produk D5", "Produk D6"] | |
| } | |
| # Daftar semua produk | |
| all_products = [p for cat in categories.values() for p in cat] | |
| # Fungsi untuk membuat dokumen PDF | |
| def create_pdf(data, filename): | |
| doc = SimpleDocTemplate(filename, pagesize=A4, rightMargin=2*cm, leftMargin=2*cm, topMargin=2*cm, bottomMargin=2*cm) | |
| styles = getSampleStyleSheet() | |
| elements = [] | |
| # Gaya teks | |
| title_style = ParagraphStyle(name='Title', fontSize=16, alignment=1, spaceAfter=12) | |
| normal_style = ParagraphStyle(name='Normal', fontSize=12, spaceAfter=8) | |
| bold_style = ParagraphStyle(name='Bold', fontSize=12, fontName='Helvetica-Bold', spaceAfter=8) | |
| # Header | |
| elements.append(Paragraph("Dokumen Penawaran", title_style)) | |
| elements.append(Paragraph("PT. Contoh Perusahaan", normal_style)) | |
| elements.append(Paragraph("Jl. Contoh Alamat No. 123", normal_style)) | |
| elements.append(Spacer(1, 0.5*cm)) | |
| # Kepada Yth. | |
| elements.append(Paragraph(f"Kepada Yth.", normal_style)) | |
| elements.append(Paragraph(f"{data['nama_prospek']}", bold_style)) | |
| elements.append(Paragraph(f"{data['alamat_prospek']}", normal_style)) | |
| elements.append(Paragraph(f"Jenis Prospek: {data['jenis_prospek']}", normal_style)) | |
| elements.append(Spacer(1, 0.5*cm)) | |
| # Tanggal | |
| elements.append(Paragraph(f"Tanggal: {data['tanggal']}", normal_style)) | |
| elements.append(Spacer(1, 0.5*cm)) | |
| # Tabel Produk | |
| elements.append(Paragraph("Daftar Produk yang Ditawarkan:", bold_style)) | |
| # Data tabel | |
| table_data = [["Nama Produk", "Jumlah", "Harga (Rp)", "Total (Rp)"]] | |
| sub_total = 0 | |
| for p in data['produk']: | |
| total = p['jumlah'] * p['harga'] | |
| sub_total += total | |
| table_data.append([p['nama'], str(p['jumlah']), f"{p['harga']:,}", f"{total:,}"]) | |
| # Hitung diskon dan grand total | |
| if 'diskon' in data and data['diskon']: | |
| diskon_percent = float(data['diskon']) | |
| diskon_amount = (diskon_percent / 100) * sub_total | |
| grand_total = sub_total - diskon_amount | |
| else: | |
| diskon_percent = 0 | |
| diskon_amount = 0 | |
| grand_total = sub_total | |
| # Tambahkan baris sub-total, diskon, dan grand total | |
| table_data.append(["", "", "Sub-Total", f"{sub_total:,}"]) | |
| if diskon_percent > 0: | |
| table_data.append(["", "", f"Diskon ({diskon_percent}%)", f"-{diskon_amount:,}"]) | |
| table_data.append(["", "", "Grand Total", f"{grand_total:,}"]) | |
| # Buat tabel | |
| table = Table(table_data, colWidths=[6*cm, 2*cm, 3*cm, 3*cm]) | |
| table.setStyle(TableStyle([ | |
| ('BACKGROUND', (0, 0), (-1, 0), colors.grey), | |
| ('TEXTCOLOR', (0, 0), (-1, 0), colors.whitesmoke), | |
| ('ALIGN', (0, 0), (-1, -1), 'CENTER'), | |
| ('FONTNAME', (0, 0), (-1, 0), 'Helvetica-Bold'), | |
| ('FONTSIZE', (0, 0), (-1, -1), 12), | |
| ('BOTTOMPADDING', (0, 0), (-1, 0), 12), | |
| ('BACKGROUND', (0, 1), (-1, -3), colors.beige), | |
| ('GRID', (0, 0), (-1, -1), 1, colors.black), | |
| ('SPAN', (0, -2), (1, -2)), # Span sub-total | |
| ('SPAN', (0, -1), (1, -1)), # Span grand total | |
| ('ALIGN', (2, -2), (3, -1), 'RIGHT'), # Align right for totals | |
| ])) | |
| if diskon_percent > 0: | |
| table.setStyle(TableStyle([ | |
| ('SPAN', (0, -3), (1, -3)), # Span diskon | |
| ])) | |
| elements.append(table) | |
| elements.append(Spacer(1, 0.5*cm)) | |
| # Syarat dan Ketentuan | |
| syarat_text = f"Syarat dan Ketentuan:<br/>{data['syarat']}" if 'syarat' in data else "Syarat dan Ketentuan: Tidak ada" | |
| elements.append(Paragraph(syarat_text, normal_style)) | |
| elements.append(Spacer(1, 0.5*cm)) | |
| # Tanda Tangan | |
| elements.append(Paragraph("Hormat kami,", normal_style)) | |
| elements.append(Paragraph("PT. Contoh Perusahaan", normal_style)) | |
| # Build PDF | |
| doc.build(elements) | |
| return filename | |
| # Fungsi untuk memproses input dan membuat penawaran | |
| def buat_penawaran( | |
| nama_prospek, alamat_prospek, jenis_prospek, tanggal, diskon, syarat, | |
| check_Produk_A1, jumlah_Produk_A1, harga_Produk_A1, | |
| check_Produk_A2, jumlah_Produk_A2, harga_Produk_A2, | |
| check_Produk_A3, jumlah_Produk_A3, harga_Produk_A3, | |
| check_Produk_A4, jumlah_Produk_A4, harga_Produk_A4, | |
| check_Produk_A5, jumlah_Produk_A5, harga_Produk_A5, | |
| check_Produk_A6, jumlah_Produk_A6, harga_Produk_A6, | |
| check_Produk_B1, jumlah_Produk_B1, harga_Produk_B1, | |
| check_Produk_B2, jumlah_Produk_B2, harga_Produk_B2, | |
| check_Produk_B3, jumlah_Produk_B3, harga_Produk_B3, | |
| check_Produk_B4, jumlah_Produk_B4, harga_Produk_B4, | |
| check_Produk_B5, jumlah_Produk_B5, harga_Produk_B5, | |
| check_Produk_B6, jumlah_Produk_B6, harga_Produk_B6, | |
| check_Produk_C1, jumlah_Produk_C1, harga_Produk_C1, | |
| check_Produk_C2, jumlah_Produk_C2, harga_Produk_C2, | |
| check_Produk_C3, jumlah_Produk_C3, harga_Produk_C3, | |
| check_Produk_C4, jumlah_Produk_C4, harga_Produk_C4, | |
| check_Produk_C5, jumlah_Produk_C5, harga_Produk_C5, | |
| check_Produk_C6, jumlah_Produk_C6, harga_Produk_C6, | |
| check_Produk_D1, jumlah_Produk_D1, harga_Produk_D1, | |
| check_Produk_D2, jumlah_Produk_D2, harga_Produk_D2, | |
| check_Produk_D3, jumlah_Produk_D3, harga_Produk_D3, | |
| check_Produk_D4, jumlah_Produk_D4, harga_Produk_D4, | |
| check_Produk_D5, jumlah_Produk_D5, harga_Produk_D5, | |
| check_Produk_D6, jumlah_Produk_D6, harga_Produk_D6 | |
| ): | |
| data = { | |
| 'nama_prospek': nama_prospek if nama_prospek else "Prospek Tanpa Nama", | |
| 'alamat_prospek': alamat_prospek if alamat_prospek else "Alamat Tidak Diketahui", | |
| 'jenis_prospek': jenis_prospek if jenis_prospek else "Individu", | |
| 'tanggal': tanggal if tanggal else datetime.date.today().strftime("%Y-%m-%d"), | |
| 'produk': [] | |
| } | |
| # Proses produk | |
| product_inputs = [ | |
| (check_Produk_A1, jumlah_Produk_A1, harga_Produk_A1, "Produk A1"), | |
| (check_Produk_A2, jumlah_Produk_A2, harga_Produk_A2, "Produk A2"), | |
| (check_Produk_A3, jumlah_Produk_A3, harga_Produk_A3, "Produk A3"), | |
| (check_Produk_A4, jumlah_Produk_A4, harga_Produk_A4, "Produk A4"), | |
| (check_Produk_A5, jumlah_Produk_A5, harga_Produk_A5, "Produk A5"), | |
| (check_Produk_A6, jumlah_Produk_A6, harga_Produk_A6, "Produk A6"), | |
| (check_Produk_B1, jumlah_Produk_B1, harga_Produk_B1, "Produk B1"), | |
| (check_Produk_B2, jumlah_Produk_B2, harga_Produk_B2, "Produk B2"), | |
| (check_Produk_B3, jumlah_Produk_B3, harga_Produk_B3, "Produk B3"), | |
| (check_Produk_B4, jumlah_Produk_B4, harga_Produk_B4, "Produk B4"), | |
| (check_Produk_B5, jumlah_Produk_B5, harga_Produk_B5, "Produk B5"), | |
| (check_Produk_B6, jumlah_Produk_B6, harga_Produk_B6, "Produk B6"), | |
| (check_Produk_C1, jumlah_Produk_C1, harga_Produk_C1, "Produk C1"), | |
| (check_Produk_C2, jumlah_Produk_C2, harga_Produk_C2, "Produk C2"), | |
| (check_Produk_C3, jumlah_Produk_C3, harga_Produk_C3, "Produk C3"), | |
| (check_Produk_C4, jumlah_Produk_C4, harga_Produk_C4, "Produk C4"), | |
| (check_Produk_C5, jumlah_Produk_C5, harga_Produk_C5, "Produk C5"), | |
| (check_Produk_C6, jumlah_Produk_C6, harga_Produk_C6, "Produk C6"), | |
| (check_Produk_D1, jumlah_Produk_D1, harga_Produk_D1, "Produk D1"), | |
| (check_Produk_D2, jumlah_Produk_D2, harga_Produk_D2, "Produk D2"), | |
| (check_Produk_D3, jumlah_Produk_D3, harga_Produk_D3, "Produk D3"), | |
| (check_Produk_D4, jumlah_Produk_D4, harga_Produk_D4, "Produk D4"), | |
| (check_Produk_D5, jumlah_Produk_D5, harga_Produk_D5, "Produk D5"), | |
| (check_Produk_D6, jumlah_Produk_D6, harga_Produk_D6, "Produk D6"), | |
| ] | |
| for check, jumlah, harga, prod in product_inputs: | |
| if check: | |
| try: | |
| j = int(jumlah) if jumlah else 0 | |
| h = int(harga) if harga else 0 | |
| if j > 0 and h > 0: | |
| data['produk'].append({'nama': prod, 'jumlah': j, 'harga': h}) | |
| except ValueError: | |
| pass # Skip invalid entries | |
| if not data['produk']: | |
| data['produk'].append({'nama': "Produk Contoh", 'jumlah': 1, 'harga': 100000}) | |
| warning = "Peringatan: Tidak ada produk yang dipilih dengan jumlah dan harga valid. Diganti dengan data contoh." | |
| else: | |
| warning = "" | |
| if diskon: | |
| try: | |
| data['diskon'] = float(diskon) | |
| except ValueError: | |
| data['diskon'] = 0 | |
| if syarat: | |
| data['syarat'] = syarat | |
| # Validasi data | |
| missing_data = [] | |
| if not nama_prospek: | |
| missing_data.append("Nama Prospek") | |
| if not alamat_prospek: | |
| missing_data.append("Alamat Prospek") | |
| if not jenis_prospek: | |
| missing_data.append("Jenis Prospek") | |
| if not tanggal: | |
| missing_data.append("Tanggal") | |
| if missing_data: | |
| warning += " Peringatan: Data berikut kurang dan telah diisi dengan asumsi: " + ", ".join(missing_data) + ". Silakan lengkapi data jika perlu." | |
| # Buat file PDF | |
| filename = ''.join(random.choices(string.ascii_letters + string.digits, k=10)) + ".pdf" | |
| pdf_file = create_pdf(data, filename) | |
| return pdf_file, warning | |
| # HTML untuk mengintegrasikan annyang | |
| annyang_html = """ | |
| <div> | |
| <button id="startSpeech">Mulai Pengenalan Suara</button> | |
| <button id="stopSpeech" style="display:none;">Hentikan Pengenalan Suara</button> | |
| <p id="speechStatus">Status: Menunggu perintah suara...</p> | |
| </div> | |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/annyang/2.6.1/annyang.min.js"></script> | |
| <script> | |
| if (annyang) { | |
| // Konfigurasi annyang untuk bahasa Indonesia | |
| annyang.setLanguage('id-ID'); | |
| // Definisikan perintah suara | |
| var commands = { | |
| 'nama *value': function(value) { | |
| document.getElementById('nama_prospek').querySelector('textarea').value = value; | |
| document.getElementById('speechStatus').innerText = 'Nama prospek diisi: ' + value; | |
| const inputEvent = new Event('input', { bubbles: true, cancelable: true }); | |
| document.getElementById('nama_prospek').querySelector('textarea').dispatchEvent(inputEvent); | |
| }, | |
| 'alamat *value': function(value) { | |
| document.getElementById('alamat_prospek').querySelector('textarea').value = value; | |
| document.getElementById('speechStatus').innerText = 'Alamat prospek diisi: ' + value; | |
| const inputEvent = new Event('input', { bubbles: true, cancelable: true }); | |
| document.getElementById('alamat_prospek').querySelector('textarea').dispatchEvent(inputEvent); | |
| }, | |
| 'jenis individu': function() { | |
| document.getElementById('jenis_prospek').querySelector('textarea').value = 'Individu'; | |
| document.getElementById('speechStatus').innerText = 'Jenis prospek: Individu'; | |
| }, | |
| 'jenis perusahaan': function() { | |
| document.getElementById('jenis_prospek').querySelector('textarea').value = 'Perusahaan'; | |
| document.getElementById('speechStatus').innerText = 'Jenis prospek: Perusahaan'; | |
| }, | |
| 'jenis kafe': function() { | |
| document.getElementById('jenis_prospek').querySelector('textarea').value = 'Kafe'; | |
| document.getElementById('speechStatus').innerText = 'Jenis prospek: Kafe'; | |
| }, | |
| 'tanggal *value': function(value) { | |
| document.getElementById('tanggal').querySelector('textarea').value = value; | |
| document.getElementById('speechStatus').innerText = 'Tanggal diisi: ' + value; | |
| const inputEvent = new Event('input', { bubbles: true, cancelable: true }); | |
| document.getElementById('tanggal').querySelector('textarea').dispatchEvent(inputEvent); | |
| }, | |
| 'pilih produk *prod': function(prod) { | |
| var checkboxes = document.querySelectorAll('input[type="checkbox"]'); | |
| console.log(checkboxes) | |
| checkboxes.forEach(function(cb) { | |
| console.log("cb:",cb,cb.id,cb.checked, prod) | |
| if (cb.id.toLowerCase().includes(prod.toLowerCase())) { | |
| cb.checked = true; | |
| console.log("OK1:",prod, cb.id ) | |
| const inputEvent = new Event('input', { bubbles: true, cancelable: true }); | |
| cb.dispatchEvent(inputEvent); | |
| } | |
| }); | |
| document.getElementById('speechStatus').innerText = 'Produk dipilih: ' + prod; | |
| }, | |
| 'jumlah produk *prod *jumlah': function(prod, jumlah) { | |
| var inputs = document.querySelectorAll('input[type="text"]'); | |
| console.log(inputs) | |
| inputs.forEach(function(input) { | |
| console.log("input1:",input, input.id ) | |
| if (input.id.includes('jumlah_') && input.id.toLowerCase().includes(prod.toLowerCase())) { | |
| console.log("OK2:",input, input.id ) | |
| input.querySelector('textarea').value = jumlah; | |
| const inputEvent = new Event('input', { bubbles: true, cancelable: true }); | |
| input.querySelector('textarea').dispatchEvent(inputEvent); | |
| } | |
| }); | |
| document.getElementById('speechStatus').innerText = 'Jumlah untuk produk ' + prod + ': ' + jumlah; | |
| }, | |
| 'harga produk *prod *harga': function(prod, harga) { | |
| var inputs = document.querySelectorAll('input[type="text"]'); | |
| console.log(inputs) | |
| inputs.forEach(function(input) { | |
| console.log("input2:",input) | |
| if (input.id.includes('harga_') && input.id.toLowerCase().includes(prod.toLowerCase())) { | |
| input.querySelector('textarea').value = harga; | |
| const inputEvent = new Event('input', { bubbles: true, cancelable: true }); | |
| input.querySelector('textarea').dispatchEvent(inputEvent); | |
| } | |
| }); | |
| document.getElementById('speechStatus').innerText = 'Harga untuk produk ' + prod + ': ' + harga; | |
| }, | |
| 'diskon *value': function(value) { | |
| document.getElementById('diskon').querySelector('textarea').value = value; | |
| document.getElementById('speechStatus').innerText = 'Diskon diisi: ' + value; | |
| const inputEvent = new Event('input', { bubbles: true, cancelable: true }); | |
| document.getElementById('diskon').querySelector('textarea').dispatchEvent(inputEvent); | |
| }, | |
| 'syarat *value': function(value) { | |
| document.getElementById('syarat').querySelector('textarea').value = value; | |
| document.getElementById('speechStatus').innerText = 'Syarat dan ketentuan diisi: ' + value; | |
| const inputEvent = new Event('input', { bubbles: true, cancelable: true }); | |
| document.getElementById('syarat').querySelector('textarea').dispatchEvent(inputEvent); | |
| } | |
| }; | |
| // Tambahkan perintah ke annyang | |
| annyang.addCommands(commands); | |
| // Event untuk tombol mulai | |
| document.getElementById('startSpeech').addEventListener('click', function() { | |
| annyang.start({ autoRestart: true, continuous: true }); | |
| document.getElementById('startSpeech').style.display = 'none'; | |
| document.getElementById('stopSpeech').style.display = 'inline'; | |
| document.getElementById('speechStatus').innerText = 'Status: Mendengarkan perintah suara...'; | |
| }); | |
| // Event untuk tombol hentikan | |
| document.getElementById('stopSpeech').addEventListener('click', function() { | |
| annyang.abort(); | |
| document.getElementById('startSpeech').style.display = 'inline'; | |
| document.getElementById('stopSpeech').style.display = 'none'; | |
| document.getElementById('speechStatus').innerText = 'Status: Pengenalan suara dihentikan.'; | |
| }); | |
| } | |
| </script> | |
| """ | |
| # Interface Gradio | |
| with gr.Blocks() as demo: | |
| gr.Markdown("# Aplikasi Pembuatan Dokumen Penawaran") | |
| # Komponen HTML untuk annyang | |
| gr.HTML(annyang_html) | |
| with gr.Row(): | |
| nama_prospek = gr.Textbox(label="Nama Prospek", elem_id="nama_prospek") | |
| alamat_prospek = gr.Textbox(label="Alamat Prospek", elem_id="alamat_prospek") | |
| jenis_prospek = gr.Dropdown(choices=["Individu", "Perusahaan", "Kafe"], label="Jenis Prospek", elem_id="jenis_prospek") | |
| with gr.Row(): | |
| tanggal = gr.Textbox(label="Tanggal Penawaran (YYYY-MM-DD)", elem_id="tanggal") | |
| with gr.Column(): | |
| gr.Markdown("## Pilih Produk, Jumlah, dan Harga") | |
| product_inputs = [] | |
| for cat, prods in categories.items(): | |
| gr.Markdown(f"### {cat}") | |
| for prod in prods: | |
| with gr.Row(): | |
| check = gr.Checkbox(label=prod, value=False, elem_id=f"check_{prod}") | |
| jumlah = gr.Textbox(label="Jumlah", elem_id=f"jumlah_{prod}") | |
| harga = gr.Textbox(label="Harga (Rp)", elem_id=f"harga_{prod}") | |
| product_inputs.extend([check, jumlah, harga]) | |
| with gr.Row(): | |
| diskon = gr.Textbox(label="Diskon (%)", elem_id="diskon") | |
| syarat = gr.Textbox(label="Syarat dan Ketentuan", elem_id="syarat") | |
| submit_button = gr.Button("Buat Penawaran") | |
| output_file = gr.File(label="Download Dokumen Penawaran (PDF)") | |
| warning_text = gr.Textbox(label="Peringatan") | |
| # Kumpulkan semua input | |
| inputs = [ | |
| nama_prospek, alamat_prospek, jenis_prospek, tanggal, diskon, syarat | |
| ] + product_inputs | |
| submit_button.click( | |
| buat_penawaran, | |
| inputs=inputs, | |
| outputs=[output_file, warning_text] | |
| ) | |
| demo.launch() |