parent
47e7b69af7
commit
ccd8977d40
@ -0,0 +1,13 @@ |
||||
package features_fakepay |
||||
|
||||
import ( |
||||
"github.com/gofiber/fiber/v2" |
||||
) |
||||
|
||||
func PostApiPay(c *fiber.Ctx) error { |
||||
return c.Redirect("/fakepay/complete") |
||||
} |
||||
|
||||
func SetupApi(app *fiber.App) { |
||||
app.Post("/api/fakepay/pay", PostApiPay) |
||||
} |
||||
@ -0,0 +1,9 @@ |
||||
package features_fakepay |
||||
|
||||
import ( |
||||
// "MY/webapp/data"
|
||||
// _ "github.com/mattn/go-sqlite3"
|
||||
// sq "github.com/Masterminds/squirrel"
|
||||
) |
||||
|
||||
|
||||
@ -0,0 +1,10 @@ |
||||
package features_fakepay |
||||
|
||||
import ( |
||||
"github.com/gofiber/fiber/v2" |
||||
) |
||||
|
||||
func Setup(app *fiber.App) { |
||||
SetupApi(app) |
||||
SetupViews(app) |
||||
} |
||||
@ -0,0 +1,11 @@ |
||||
package features_fakepay |
||||
|
||||
import ( |
||||
"github.com/gofiber/fiber/v2" |
||||
. "MY/webapp/common" |
||||
) |
||||
|
||||
func SetupViews(app *fiber.App) { |
||||
err := ConfigViews(app, "views/fakepay") |
||||
if err != nil { panic(err) } |
||||
} |
||||
@ -0,0 +1,233 @@ |
||||
const $boot = (cb) => { |
||||
document.addEventListener("DOMContentLoaded", cb); |
||||
} |
||||
|
||||
const $sanitize = (badHTML) => { |
||||
// this Option hack courtesy of stackoverflow
|
||||
return new Option(badHTML).innerHTML; |
||||
} |
||||
|
||||
const validate_sanitized = (sanitized) => { |
||||
const check = JSON.stringify(sanitized); |
||||
console.assert(!check.includes("<"), |
||||
"YOU BEEN HACKED! Send Zed this:", sanitized); |
||||
} |
||||
|
||||
const $sanitize_data = (data) => { |
||||
const sanitized = $sanitize(JSON.stringify(data)); |
||||
validate_sanitized(sanitized); |
||||
return JSON.parse(sanitized); |
||||
} |
||||
|
||||
const $render = (template, data) => { |
||||
const sanitized = $sanitize_data(data); |
||||
|
||||
const args = Object.keys(sanitized); |
||||
const values = Object.values(sanitized) |
||||
|
||||
const render_func = new Function(...args, |
||||
`return \`${template.innerHTML}\``) |
||||
|
||||
const text = render_func(...values); |
||||
|
||||
return $html(text); |
||||
} |
||||
|
||||
const $render_data = (template_id, target_id, data) => { |
||||
const target = $id(target_id); |
||||
const template = $id(template_id); |
||||
let new_data = []; |
||||
|
||||
if(Array.isArray(data)) { |
||||
new_data = data.map((item, i) => { |
||||
return $render(template, {i, item}); |
||||
}); |
||||
} else if(data instanceof Object) { |
||||
console.log("object rendering", data); |
||||
new_data.push($render(template, data)); |
||||
} else { |
||||
console.error("$render only works with {} or [] data", data); |
||||
new_data.push($html("<b>ERROR LOOK IN CONSOLE</b>")); |
||||
} |
||||
|
||||
$replace_with(target, new_data); |
||||
} |
||||
|
||||
const $get = (name, inChildren) => { |
||||
if(inChildren) { |
||||
return inChildren.querySelector(name); |
||||
} else { |
||||
return document.querySelector(name); |
||||
} |
||||
} |
||||
|
||||
const $all = (name, inChildren) => { |
||||
if(inChildren) { |
||||
return inChildren.querySelectorAll(name); |
||||
} else { |
||||
return document.querySelectorAll(name); |
||||
} |
||||
} |
||||
|
||||
const $id = (name, inChildren) => { |
||||
if(inChildren) { |
||||
return inChildren.children.namedItem(name); |
||||
} else { |
||||
return document.getElementById(name); |
||||
} |
||||
} |
||||
|
||||
const $class = (name) => { |
||||
return document.getElementsByClassName(name); |
||||
} |
||||
|
||||
const $name = (name) => { |
||||
return document.getElementsByTagName(name); |
||||
} |
||||
|
||||
const $filter = (nodes, fn) => { |
||||
return Array.prototype.filter.call(nodes, fn); |
||||
} |
||||
|
||||
const $next = (node) => { |
||||
return node.nextElementSibling; |
||||
} |
||||
|
||||
const $previous = (node) => { |
||||
return node.previousElementSibling; |
||||
} |
||||
|
||||
const $siblings = (node) => { |
||||
return $filter(node.parentNode.children,
|
||||
child => { return child !== node; }); |
||||
} |
||||
|
||||
const $style_toggle = (node, className) => { |
||||
node.classList.toggle(className); |
||||
} |
||||
|
||||
const $style_add = (node, className) => { |
||||
node.classList.add(className); |
||||
} |
||||
|
||||
const $style_del = (node, className) => { |
||||
node.classList.remove(className); |
||||
} |
||||
|
||||
const $style_of = (node, ruleName) => { |
||||
return getComputedStyle(node)[ruleName] |
||||
} |
||||
|
||||
const $new = (tag, id, html) => { |
||||
let new_tag = document.createElement(tag); |
||||
new_tag.id = id; |
||||
new_tag.innerHTML = html; |
||||
return new_tag.cloneNode(true); |
||||
} |
||||
|
||||
const $append = (parent, child) => { |
||||
parent.appendChild(child); |
||||
} |
||||
|
||||
const $prepend = (parent, node) => { |
||||
parent.insertBefore(node, parent.firstChild); |
||||
} |
||||
|
||||
const $remove = (parent, child) => { |
||||
parent.removeChild(child); |
||||
} |
||||
|
||||
const $clone = (node) => { |
||||
return node.cloneNode(true); |
||||
} |
||||
|
||||
const $contains = (node, child) => { |
||||
return node !== child && node.contains(child); |
||||
} |
||||
|
||||
const $has = (node, selector) => { |
||||
return node.querySelector(selector) !== null; |
||||
} |
||||
|
||||
const $empty = (node) => { |
||||
return node.innerHTML == ''; |
||||
} |
||||
|
||||
const $attribute_of = (node, name) => { |
||||
return node.getAttribute(name); |
||||
} |
||||
|
||||
const $attribute = (node, name, value) => { |
||||
return node.setAttribute(name, value); |
||||
} |
||||
|
||||
const $contents_of = (node) => { |
||||
return node.innerHTML; |
||||
} |
||||
|
||||
const $contents = (node, newhtml) => { |
||||
node.innerHTML = newhtml; |
||||
} |
||||
|
||||
const $has_class = (node, className) => { |
||||
return node.classList.contains(className); |
||||
} |
||||
|
||||
const $outer_html = (node) => { |
||||
return node.outerHTML; |
||||
} |
||||
|
||||
const $replace_with = (node, newNodes) => { |
||||
node.replaceChildren(...newNodes); |
||||
} |
||||
|
||||
const $matches = (node, selector) => { |
||||
return node.matches(selector); |
||||
} |
||||
|
||||
const $parent = (node) => { |
||||
return node.parentNode; |
||||
} |
||||
|
||||
const $text_of = (node) => { |
||||
return node.textContent; |
||||
} |
||||
|
||||
const $text = (node, newtext) => { |
||||
node.textContent = newtext; |
||||
} |
||||
|
||||
const $off = (node, eventName, eventHandler) => { |
||||
node.removeEventListener(eventName, eventHandler); |
||||
} |
||||
|
||||
const $on = (node, eventName, eventHandler) => { |
||||
node.addEventListener(eventName, eventHandler); |
||||
} |
||||
|
||||
const $now = () => { |
||||
return Date.now(); |
||||
} |
||||
|
||||
const $html = (htmlString) => { |
||||
const temp = document.createElement('template'); |
||||
temp.innerHTML = htmlString; |
||||
return temp.content; |
||||
} |
||||
|
||||
const $observe = (node_id, cb) => { |
||||
const options = { |
||||
root: document, |
||||
rootMargin: "0px", |
||||
scrollMargin: "0px", |
||||
threshold: 1.0, |
||||
delay: 200, |
||||
}; |
||||
|
||||
const callback = (entries, observer) => { |
||||
cb(); |
||||
} |
||||
|
||||
const observer = new IntersectionObserver(callback, options); |
||||
observer.observe($id(node_id)); |
||||
} |
||||
@ -0,0 +1,14 @@ |
||||
package tests |
||||
|
||||
import ( |
||||
"testing" |
||||
"github.com/stretchr/testify/require" |
||||
) |
||||
|
||||
func TestLogin(t *testing.T) { |
||||
assert.Equal(true, false) |
||||
} |
||||
|
||||
func TestMain(m *testing.M) { |
||||
m.Run() |
||||
} |
||||
@ -0,0 +1,6 @@ |
||||
<h1>Thank You!</h1> |
||||
|
||||
<p>Your payment has been accepted and you should receive an email with your receipt.<p> |
||||
|
||||
<p><a href="/login/">Log In To Your Account</a></p> |
||||
|
||||
@ -0,0 +1,29 @@ |
||||
|
||||
<card> |
||||
<top> |
||||
<h1>FakePay</h1> |
||||
</top> |
||||
<middle> |
||||
<form action="/api/fakepay/pay" method="POST"> |
||||
<label for="Card">Card</label> |
||||
<input name="Card" id="Card" placeholder="Card" /> |
||||
|
||||
<label for="NameOnCard">Name on Card</label> |
||||
<input name="NameOnCard" id="NameOnCard" placeholder="Name on Card" /> |
||||
|
||||
<label for="CVV">CVV</label> |
||||
<input name="CVV" id="CVV" placeholder="CVV" /> |
||||
|
||||
<label for="Expiration">Expiration</label> |
||||
<input name="Expiration" id="Expiration" placeholder="Expiration" /> |
||||
|
||||
<label for="PostalCode">Postal/Zip</label> |
||||
<input name="PostalCode" id="PostalCode" placeholder="PostalCode" /> |
||||
|
||||
<button-group> |
||||
<a href="/shopping/"><button type="button">Continue Shopping</button></a> |
||||
<button type="submit">Submit</button> |
||||
</button-group> |
||||
</form> |
||||
</middle> |
||||
</card> |
||||
@ -1,24 +1,104 @@ |
||||
<script> |
||||
let Data = new PaginateTable("/api/shopping/products") |
||||
/* |
||||
* Advantages of => over function: |
||||
* - Easier transition from variables to callbacks. |
||||
* - this variable is locked so isn't random |
||||
* - can't accidentally rename the function because it's const |
||||
* - callbacks with function are fucking stupid. |
||||
*/ |
||||
let Data = new ForeverScroll("/api/shopping/products"); |
||||
|
||||
const cartItems = []; |
||||
|
||||
const update = () => { |
||||
// product list |
||||
// cart contents |
||||
const cart = $id('cart'); |
||||
let total = cartItems.reduce((a, v) => a + v.Price, 0.0); |
||||
|
||||
if(cartItems.length > 0) { |
||||
$style_del(cart, 'hidden'); |
||||
$render_data('cart-row', 'cart-items', cartItems); |
||||
} else { |
||||
$style_add(cart, 'hidden'); |
||||
} |
||||
|
||||
$text($id('cart-total'), `${total}`); |
||||
} |
||||
|
||||
const remove = (item_number) => { |
||||
cartItems.splice(item_number, 1); |
||||
update(); |
||||
} |
||||
|
||||
const addToCart = (item_i) => { |
||||
cartItems.push(Data.items[item_i]); |
||||
update(); |
||||
} |
||||
|
||||
$boot(async () => { |
||||
// kind of not good so revisit how Data works |
||||
const items = await Data.init(); |
||||
const list = $id('product-list'); |
||||
const tmpl = $id('product-row'); |
||||
|
||||
for(let i in items) { |
||||
$append(list, $render(tmpl, {i, item: items[i]})); |
||||
} |
||||
}); |
||||
|
||||
</script> |
||||
|
||||
<style> |
||||
.hidden { |
||||
visibility: hidden; |
||||
} |
||||
</style> |
||||
|
||||
<h1>Products</h1> |
||||
|
||||
<p>I have these products for you:</p> |
||||
|
||||
<block x-data="Data"> |
||||
<template x-for="item in contents"> |
||||
<bar class="bg-gray-800"> |
||||
<shape x-text="item.Slug"></shape> |
||||
<div> |
||||
<h4 x-text="item.Title"></h4> |
||||
<aside x-text="item.Description"></aside> |
||||
|
||||
<bar class="justify-between" style="padding:30px"> |
||||
<div class="text-2xl">Price: <span x-text="item.Price"></span></div> |
||||
<a href="/shopping/checkout"><button type="button">Buy</button></a> |
||||
</bar> |
||||
</div> |
||||
</bar> |
||||
</template> |
||||
<div id="cart" class="hidden"> |
||||
<aside> |
||||
<table id='cart-items'> |
||||
</table> |
||||
|
||||
<template id="cart-row"> |
||||
<tr id='item-${i}'> |
||||
<td>${item.Title}</td> |
||||
<td>${item.Price}</td> |
||||
<td> |
||||
<button type="button" onclick='remove(${i})'>X</button> |
||||
</td> |
||||
</tr> |
||||
</template> |
||||
|
||||
<p>Cart total is <span id='cart-total'></span></p> |
||||
<a href="/shopping/checkout"> |
||||
<button type="button">Checkout</button> |
||||
</a> |
||||
</aside> |
||||
</div> |
||||
|
||||
<block id='product-list'> |
||||
<template id='product-row'> |
||||
<bar class="bg-gray-800"> |
||||
<shape></shape> |
||||
<div> |
||||
<h4>${item.Title}</h4> |
||||
<aside>${item.Description}</aside> |
||||
|
||||
<bar class="justify-between" style="padding:30px"> |
||||
<div class="text-2xl">Price: <span>${item.Price}</span></div> |
||||
<button onclick="addToCart(${i})" id="cart-button" type="button"> |
||||
<span> |
||||
Add to Cart |
||||
</span> |
||||
</button> |
||||
</bar> |
||||
</div> |
||||
</bar> |
||||
</template> |
||||
</block> |
||||
|
||||
<div id='canary'></div> |
||||
|
||||
|
||||
Loading…
Reference in new issue