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() |