package main
import (
"context"
"fmt"
"time"
"github.com/fastly/compute-sdk-go/fsthttp"
"github.com/flosch/pongo2/v6"
)
type invoiceItem struct {
Name string
Price float64
}
func main() {
fsthttp.ServeFunc(func(ctx context.Context, w fsthttp.ResponseWriter, r *fsthttp.Request) {
invoiceItems := []invoiceItem{
{"Pet Food", 19.99},
{"Leash", 10.75},
{"Shampoo", 9.99},
}
totalPrice := 0.0
for _, item := range invoiceItems {
totalPrice += item.Price
}
now := time.Now()
due := now.AddDate(0, 0, 30)
tplCtx := pongo2.Context{
"page_title": "Sample Invoice, powered by Fastly",
"vendor_title": "Fastly Invoice",
"address": "P.O. Box 78266<br/>San Francisco, CA 94107 ",
"customer": "Example Corp.<br />billing@example.com",
"payment": "Credit card",
"date_created": now.Format("Jan _2 2006"),
"date_due": due.Format("Jan _2 2006"),
"invoice_items": invoiceItems,
"total_price": totalPrice,
"invoice_num": "10937248",
"css_style": invoiceStyle,
}
out, err := invoiceTemplateTpl.Execute(tplCtx)
if err != nil {
w.WriteHeader(500)
fmt.Fprintf(w, "Error executing template")
return
}
w.Header().Set("Content-Type", "text/html; charset=utf-8")
fmt.Fprintln(w, out)
})
}
const invoiceTemplate = `{% autoescape off %}<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>{{page_title}}</title>
<style>{{css_style}}</style>
</head>
<body>
<div class="invoice-box">
<table cellpadding="0" cellspacing="0">
<tr class="top">
<td colspan="2">
<table>
<tr>
<td class="title">{{vendor_title}}</td>
<td>
Invoice #: {{invoice_num}}<br />
Created: {{date_created}}<br />
Due: {{date_due}}
</td>
</tr>
</table>
</td>
</tr>
<tr class="information">
<td colspan="2">
<table>
<tr>
<td>{{address}}</td>
<td>{{customer}}</td>
</tr>
</table>
</td>
</tr>
<tr class="heading"><td>Item</td><td>Price</td></tr>
{% for item in invoice_items %}
<tr class="item">
<td>{{item.Name}}</td>
<td>${{item.Price|floatformat:2}}</td>
</tr>
{% endfor %}
<tr class="total">
<td></td>
<td>Total: ${{total_price|floatformat:2}}</td>
</tr>
</table>
</div>
</body>
</html>{% endautoescape %}
`
var invoiceTemplateTpl = pongo2.Must(pongo2.FromString(invoiceTemplate))
const invoiceStyle = `.invoice-box {
max-width: 800px;
margin: auto;
padding: 30px;
border: 1px solid #eee;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.15);
font-size: 16px;
line-height: 24px;
font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif;
color: #555;
}
.invoice-box table {
width: 100%;
line-height: inherit;
text-align: left;
}
.invoice-box table td {
padding: 5px;
vertical-align: top;
}
.invoice-box table tr td:nth-child(2) {
text-align: right;
}
.invoice-box table tr.top table td {
padding-bottom: 20px;
}
.invoice-box table tr.top table td.title {
font-size: 45px;
line-height: 45px;
color: #333;
}
.invoice-box table tr.information table td {
padding-bottom: 40px;
}
.invoice-box table tr.heading td {
background: #eee;
border-bottom: 1px solid #ddd;
font-weight: bold;
}
.invoice-box table tr.details td {
padding-bottom: 20px;
}
.invoice-box table tr.item td {
border-bottom: 1px solid #eee;
}
.invoice-box table tr.item.last td {
border-bottom: none;
}
.invoice-box table tr.total td:nth-child(2) {
border-top: 2px solid #eee;
font-weight: bold;
}
@media only screen and (max-width: 600px) {
.invoice-box table tr.top table td {
width: 100%;
display: block;
text-align: center;
}
.invoice-box table tr.information table td {
width: 100%;
display: block;
text-align: center;
}
}
/** RTL **/
.invoice-box.rtl {
direction: rtl;
font-family: Tahoma, 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif;
}
.invoice-box.rtl table {
text-align: right;
}
.invoice-box.rtl table tr td:nth-child(2) {
text-align: left;
}
`