Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
duncanmcclean committed Feb 8, 2025
1 parent 1f50b81 commit 5175454
Show file tree
Hide file tree
Showing 28 changed files with 1,310 additions and 22 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,11 @@ jobs:
uses: tj-actions/changed-files@v44
with:
files: |
.github/workflows/tests.yaml
src/Payments
src/Http/Controllers/Payments
tests/Feature/Payments
composer.json
.github/workflows/tests.yaml
- name: Determine whether payment tests should run
id: should-run-payment-tests
Expand Down
85 changes: 85 additions & 0 deletions resources/views/checkout-stubs/components/_address.antlers.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
{{#
@name Address
@desc Renders address form fields.
@param* type The type of address. Options: shipping, billing.
#}}

<div x-data="{{ type | ucfirst }}Address" class="flex flex-col space-y-6">
{{ partial:checkout/components/select name="{type}_country" label="Country" autocomplete="{type} country" required="true" x-model="country" }}
<option disabled value="">{{ 'Select a country' | trans }}</option>
{{ dictionary:countries emojis="false" }}
<option value="{{ value }}">{{ label }}</option>
{{ /dictionary:countries }}
{{ /partial:checkout/components/select }}

{{ partial:checkout/components/input
name="{type}_line_1"
label="{type | ucfirst} Line 1"
type="text"
xBindValue="cart.{type}_line_1"
autocomplete="{type} address-line1"
required="true"
}}

{{ partial:checkout/components/input
name="{type}_line_2"
label="{type | ucfirst} Line 2"
type="text"
xBindValue="cart.{type}_line_2"
autocomplete="{type} address-line2"
}}

{{ partial:checkout/components/input
name="{type}_city"
label="Town/City"
type="text"
xBindValue="cart.{type}_city"
autocomplete="{type} address-level1"
required="true"
}}

{{ partial:checkout/components/input
name="{type}_postcode"
label="Postcode"
type="text"
xBindValue="cart.{type}_postcode"
autocomplete="{type} postal-code"
required="true"
}}

<template x-if="states.length">
{{ partial:checkout/components/select name="{type}_state" label="State/County" autocomplete="{type} address-level1" required="true" }}
<option disabled value="">{{ 'Select a state' | trans }}</option>
<template x-for="state in states" :key="state.code">
<option :value="state.code" :selected="state.code === cart.{{ type }}_state" x-text="state.name"></option>
</template>
{{ /partial:checkout/components/select }}
</template>
</div>

{{ push:footer_scripts }}
<script>
document.addEventListener('alpine:init', () => {
Alpine.data('{{ type | ucfirst }}Address', () => ({
country: null,
states: [],
init() {
this.country = this.cart.{{ type }}_country?.value;
if (this.country) this.fetchStates();
this.$watch('country', (value) => this.fetchStates());
},
fetchStates() {
this.states = [];
fetch(`{{ route:statamic.simple-commerce.states }}?country=${this.country}`)
.then(response => response.json())
.then(data => this.states = data);
},
}));
});
</script>
{{ /push:footer_scripts }}
12 changes: 12 additions & 0 deletions resources/views/checkout-stubs/components/_banner.antlers.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{{#
@name Banner
@desc Displays a full-width banner at the top of the Checkout page. Used for displaying "global" error messages.
#}}

{{ get_error:checkout }}
<div class="relative w-full bg-red-700 z-60">
<div class="max-w-6xl mx-auto px-8 py-3">
<p class="text-center text-white font-semibold">{{ message }}</p>
</div>
</div>
{{ /get_error:checkout }}
22 changes: 22 additions & 0 deletions resources/views/checkout-stubs/components/_button.antlers.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{{#
@name Button
@desc Renders a button.
@param type string The type of the button. Default: "submit".
@param disabled boolean Alpine.js logic to determine if the button is disabled.
@param xOnClick Alpine.js click event handler.
@param* label string The button label.
#}}

<button
type="{{ type ?? 'submit' }}"
:disabled="{{ disabled || 'busy' }}"
{{ if xOnClick }} @click="{{ xOnClick }}" {{ /if }}
class="w-full flex items-center justify-center rounded-md border border-transparent bg-secondary hover:bg-secondary/90 px-4 py-1.5 text-base font-medium text-white shadow-sm hover:bg-secondary focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 disabled:cursor-not-allowed"
>
<svg x-show="{{ disabled || 'busy' }}" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" class="w-8 h-8 animate-spin text-white/75">
<style>@keyframes spinner_svv2{to{transform:rotate(360deg)}}</style>
<path d="M10.14 1.16a11 11 0 0 0-9 8.92A1.59 1.59 0 0 0 2.46 12a1.52 1.52 0 0 0 1.65-1.3 8 8 0 0 1 6.66-6.61A1.42 1.42 0 0 0 12 2.69a1.57 1.57 0 0 0-1.86-1.53Z" style="transform-origin:center;animation:spinner_svv2 1.5s infinite linear" fill="currentColor"/>
</svg>
<span x-show="! ({{ disabled || 'busy' }})" class="py-1">{{ label | trans }}</span>
</button>
153 changes: 153 additions & 0 deletions resources/views/checkout-stubs/components/_cart_summary.antlers.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
{{#
@name Cart Summary
@desc Renders the cart summary on the Checkout page.
#}}

<template x-if="cart">
<div class="p-8 flex flex-col space-y-6 text-sm">
<ul class="flex flex-col space-y-6">
{{ cart:line_items }}
<li class="flex items-center justify-between">
<div class="flex items-center">
{{ if product:image }}
<a href="{{ product:url }}" target="_blank">
<picture>
<source srcset="{{ glide :src="product:image" square="64" format="webp" }}" type="image/webp">
<img src="{{ glide :src="product:image" square="64" }}" alt="{{ product:title }}" class="w-16 h-16 rounded-md mr-2 hover:overflow-hidden hover:scale-[1.05] transition">
</picture>
</a>
{{ /if }}
<div class="flex flex-col space-y-0.5">
<a href="{{ product:url }}" target="_blank" class="block font-semibold">{{ product:title }}</a>
{{ if variant }}
<span class="text-xs text-gray-500">{{ variant:name }}</span>
{{ /if }}
<span class="text-xs text-gray-500">{{ 'Quantity' | trans }}: {{ quantity }}</span>
</div>
</div>

<span>{{ sub_total }}</span>
</li>
{{ /cart:line_items }}
</ul>

<template x-if="!cart.coupon && !cart.is_free">
{{ cart:update x-data="ApplyCouponForm" @submit.prevent="applyCoupon" }}
<div class="flex items-center">
<input
type="text"
name="coupon"
placeholder="Coupon code"
x-model="coupon"
class="block w-full rounded-md border-gray-300 shadow-sm focus:border-secondary focus:ring-secondary sm:text-sm"
autocomplete="off"
>
<button
type="submit"
class="ml-4 h-full rounded-md border border-transparent bg-secondary px-4 py-2 text-sm font-medium text-white shadow-sm hover:bg-secondary focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 disabled:cursor-not-allowed disabled:bg-gray-100 disabled:text-gray-500"
:disabled="!coupon || busy"
>
{{ 'Apply' | trans }}
</button>
</div>

<template x-if="error">
<p class="text-red-600 text-sm mt-1" x-text="error"></p>
</template>
{{ /cart:update }}
</template>

<div class="flex items-center justify-between">
<span>{{ 'Subtotal' | trans }}</span>
<span x-text="cart.sub_total"></span>
</div>

<template x-if="cart.coupon">
<div class="flex items-center justify-between">
<div class="flex items-center space-x-1">
<span>{{ 'Coupon Discount' | trans }}</span>
<span class="text-xs text-gray-500" x-text="`(${cart.coupon.code})`"></span>
{{ cart:update class="flex items-center" x-data="RemoveCouponForm" @submit.prevent="removeCoupon" }}
<input type="hidden" name="coupon" value="">

<button class="text-xs text-gray-500 hover:text-black" title="Remove coupon">
<svg class="h-4 w-4" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18 18 6M6 6l12 12" />
</svg>
</button>
{{ /cart:update }}
</div>
<span x-text="`-${cart.discount_total}`"></span>
</div>
</template>

<div class="flex items-center justify-between">
<span>{{ 'Shipping' | trans }}</span>

<template x-if="cart.shipping_option">
<span x-text="cart.shipping_option.price"></span>
</template>

<template x-if="!cart.shipping_option">
<span class="text-gray-500">{{ 'Enter shipping address' | trans }}</span>
</template>
</div>

{{ if !config:statamic:simple-commerce:taxes:price_includes_tax }}
<div class="flex items-center justify-between">
<span>{{ 'Taxes' | trans }}</span>
<span x-text="cart.tax_total"></span>
</div>
{{ /if }}

<div class="flex items-center justify-between font-bold text-lg">
<span>{{ 'Total' | trans }}</span>
<span x-text="`{{ site:attributes:currency | upper }} ${cart.grand_total}`"></span>
</div>
</div>
</template>

{{ push:footer_scripts }}
<script>
document.addEventListener('alpine:init', () => {
Alpine.data('ApplyCouponForm', () => ({
coupon: null,
busy: false,
error: null,
async applyCoupon(e) {
e.preventDefault();
this.busy = true;
this.error = null;
await this.submit(this.$root)
.then(data => this.coupon = null)
.catch(error => {
if (error.status === 422) {
error.json().then(data => {
this.error = data.message;
});
return;
}
alert(`Something went wrong while redeeming the coupon. Please try again later.`);
})
.finally(() => this.busy = false);
},
}));
Alpine.data('RemoveCouponForm', () => ({
async removeCoupon(e) {
e.preventDefault();
await this.submit(this.$root)
.catch(error => {
alert(`Something went wrong while removing the coupon. Please try again later.`);
});
},
}));
});
</script>
{{ /push:footer_scripts }}
40 changes: 40 additions & 0 deletions resources/views/checkout-stubs/components/_input.antlers.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
{{#
@name Input
@desc A styled <input> field.
@param* name The name of the input field.
@param* label The label for the input field.
@param type The type of input field. Default: text.
@param autocomplete The autocomplete attribute for the input field.
@param placeholder The placeholder for the input field.
@param value The value for the input field.
@param xBindValue The name of an Alpine.js property to bind the input field to.
@param required Boolean. Whether the input field is required.
@param disabled Boolean. Whether the input field is disabled.
#}}

<div>
<label for="{{ name }}" class="block text-sm font-medium text-gray-700">{{ label | trans }}</label>

<div class="mt-1">
<input
type="{{ type ?? 'text' }}"
id="{{ name }}"
name="{{ name }}"
{{ if autocomplete }}autocomplete="{{ autocomplete }}"{{ /if }}
{{ if placeholder }}placeholder="{{ placeholder | trans }}"{{ /if }}
{{ if value }}value="{{ value }}"{{ /if }}
{{ if xBindValue }}:value="{{ xBindValue }}"{{ /if }}
{{ if required }}required{{ /if }}
{{ if disabled }}disabled readonly{{ /if }}
class="block w-full rounded-md border-gray-300 shadow-sm focus:border-secondary focus:ring-secondary sm:text-sm disabled:bg-gray-100 disabled:text-gray-500 disabled:cursor-not-allowed"
>
</div>

<template x-if="errors.hasOwnProperty('{{ name }}')">
<ul class="mt-1">
<template x-for="error in errors['{{ name }}']">
<li class="text-red-600 text-sm" x-text="error"></li>
</template>
</ul>
</template>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
{{#
@name Order Summary
@desc Renders the order summary on the confirmation page.
#}}

<div class="p-8 flex flex-col space-y-6 text-sm">
<ul class="flex flex-col space-y-6">
{{ line_items }}
<li class="flex items-center justify-between">
<div class="flex items-center">
{{ if product:image }}
<a href="{{ product:url }}" target="_blank">
<picture>
<source srcset="{{ glide :src="product:image" square="64" format="webp" }}" type="image/webp">
<img src="{{ glide :src="product:image" square="64" }}" alt="{{ product:title }}" class="w-16 h-16 rounded-md mr-2 hover:overflow-hidden hover:scale-[1.05] transition">
</picture>
</a>
{{ /if }}
<div class="flex flex-col space-y-0.5">
<a href="{{ product:url }}" target="_blank" class="block font-semibold">{{ product:title }}</a>
{{ if variant }}
<span class="text-xs text-gray-500">{{ variant:name }}</span>
{{ /if }}
<span class="text-xs text-gray-500">{{ 'Quantity' | trans }}: {{ quantity }}</span>
</div>
</div>

<span>{{ sub_total }}</span>
</li>
{{ /line_items }}
</ul>

<div class="flex items-center justify-between">
<span>{{ 'Subtotal' | trans }}</span>
<span>{{ sub_total }}</span>
</div>

{{ if coupon }}
<div class="flex items-center justify-between">
<div class="flex items-center space-x-1">
<span>{{ 'Coupon Discount' | trans }}</span>
<span class="text-xs text-gray-500">{{ coupon:code }}</span>
</div>
<span>-{{ discount_total }}</span>
</div>
{{ /if }}

{{ if shipping_option }}
<div class="flex items-center justify-between">
<span>{{ 'Shipping' | trans }}</span>
<span>{{ shipping_total }}</span>
</div>
{{ /if }}

{{ if !config:statamic:simple-commerce:taxes:price_includes_tax }}
<div class="flex items-center justify-between">
<span>{{ 'Taxes' | trans }}</span>
<span>{{ tax_total }}</span>
</div>
{{ /if }}

<div class="flex items-center justify-between font-bold text-lg">
<span>{{ 'Total' | trans }}</span>
<span>{{ site:attributes:currency | upper }} {{ grand_total }}</span>
</div>
</div>
Loading

0 comments on commit 5175454

Please sign in to comment.