@spwig/theme-cli 1.0.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/CHANGELOG.md +77 -0
  2. package/README.md +0 -0
  3. package/dist/cli.d.ts +0 -0
  4. package/dist/cli.d.ts.map +0 -0
  5. package/dist/cli.js +14 -22
  6. package/dist/cli.js.map +1 -1
  7. package/dist/commands/component.d.ts +0 -0
  8. package/dist/commands/component.d.ts.map +0 -0
  9. package/dist/commands/component.js +0 -0
  10. package/dist/commands/component.js.map +0 -0
  11. package/dist/commands/dev.d.ts +14 -0
  12. package/dist/commands/dev.d.ts.map +1 -0
  13. package/dist/commands/dev.js +310 -0
  14. package/dist/commands/dev.js.map +1 -0
  15. package/dist/commands/init.d.ts +5 -1
  16. package/dist/commands/init.d.ts.map +1 -1
  17. package/dist/commands/init.js +80 -115
  18. package/dist/commands/init.js.map +1 -1
  19. package/dist/commands/package.d.ts +0 -0
  20. package/dist/commands/package.d.ts.map +1 -1
  21. package/dist/commands/package.js +0 -2
  22. package/dist/commands/package.js.map +1 -1
  23. package/dist/commands/validate.d.ts +1 -2
  24. package/dist/commands/validate.d.ts.map +1 -1
  25. package/dist/commands/validate.js +10 -112
  26. package/dist/commands/validate.js.map +1 -1
  27. package/dist/index.d.ts +0 -2
  28. package/dist/index.d.ts.map +1 -1
  29. package/dist/index.js +0 -1
  30. package/dist/index.js.map +1 -1
  31. package/dist/schemas/component_manifest_schema.json +0 -0
  32. package/dist/schemas/theme_manifest_schema.json +5 -63
  33. package/dist/templates/assets/logo.svg.template +6 -0
  34. package/dist/templates/assets/theme.js.template +150 -0
  35. package/dist/templates/components/blank.template.html.template +0 -0
  36. package/dist/templates/components/footer.manifest.json.template +0 -0
  37. package/dist/templates/components/footer.schema.json.template +22 -0
  38. package/dist/templates/components/footer.styles.css.template +80 -0
  39. package/dist/templates/components/footer.template.html.template +0 -0
  40. package/dist/templates/components/header.manifest.json.template +0 -0
  41. package/dist/templates/components/header.schema.json.template +0 -0
  42. package/dist/templates/components/header.styles.css.template +104 -0
  43. package/dist/templates/components/header.template.html.template +0 -0
  44. package/dist/templates/components/schema.json.template +0 -0
  45. package/dist/templates/components/section.manifest.json.template +2 -2
  46. package/dist/templates/components/section.schema.json.template +40 -0
  47. package/dist/templates/components/section.styles.css.template +60 -0
  48. package/dist/templates/components/section.template.html.template +0 -0
  49. package/dist/templates/components/utility.manifest.json.template +0 -0
  50. package/dist/templates/pages/cart.html.template +104 -0
  51. package/dist/templates/pages/collection.html.template +86 -0
  52. package/dist/templates/pages/home.html.template +51 -0
  53. package/dist/templates/pages/layout.html.template +48 -0
  54. package/dist/templates/pages/product.html.template +90 -0
  55. package/dist/templates/presets/footer.json.template +16 -0
  56. package/dist/templates/presets/header.json.template +16 -0
  57. package/dist/templates/theme/README.md.template +51 -21
  58. package/dist/templates/theme/design_tokens.json.template +0 -0
  59. package/dist/templates/theme/manifest.json.template +6 -28
  60. package/dist/templates/theme/tokens.json.template +1254 -0
  61. package/dist/utils/file-system.d.ts +0 -0
  62. package/dist/utils/file-system.d.ts.map +0 -0
  63. package/dist/utils/file-system.js +0 -0
  64. package/dist/utils/file-system.js.map +0 -0
  65. package/dist/utils/validation.d.ts +0 -0
  66. package/dist/utils/validation.d.ts.map +0 -0
  67. package/dist/utils/validation.js +0 -0
  68. package/dist/utils/validation.js.map +0 -0
  69. package/package.json +6 -3
  70. package/schemas/theme_manifest_schema.json +5 -63
  71. package/schemas/component_manifest_schema.json +0 -221
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "http://json-schema.org/draft-07/schema#",
3
3
  "title": "Theme Package Manifest Schema",
4
- "description": "Schema for Spwig theme package manifests with bundled components",
4
+ "description": "Schema for Spwig v2.0 theme package manifests",
5
5
  "type": "object",
6
6
  "required": [
7
7
  "name",
@@ -41,74 +41,16 @@
41
41
  "maxLength": 100,
42
42
  "description": "Theme author/vendor name"
43
43
  },
44
+ "sdk_version": {
45
+ "type": "string",
46
+ "description": "SDK version used to create this theme (e.g., '2.0')"
47
+ },
44
48
  "license": {
45
49
  "type": "string",
46
50
  "enum": ["MIT", "Apache-2.0", "GPL-3.0", "Proprietary"],
47
51
  "default": "Proprietary",
48
52
  "description": "Theme license type"
49
53
  },
50
- "bundled_components": {
51
- "type": "array",
52
- "items": {
53
- "type": "object",
54
- "required": ["type", "name", "path"],
55
- "properties": {
56
- "type": {
57
- "type": "string",
58
- "enum": ["header", "footer", "section", "utility"],
59
- "description": "Component type"
60
- },
61
- "name": {
62
- "type": "string",
63
- "pattern": "^[a-z][a-z0-9_-]*$",
64
- "description": "Component name"
65
- },
66
- "path": {
67
- "type": "string",
68
- "pattern": "^components/",
69
- "description": "Relative path to component directory"
70
- }
71
- }
72
- },
73
- "description": "List of components bundled with this theme"
74
- },
75
- "page_schemas": {
76
- "type": "object",
77
- "properties": {
78
- "home": {
79
- "type": "string",
80
- "description": "Path to home page schema"
81
- },
82
- "product": {
83
- "type": "string",
84
- "description": "Path to product page schema"
85
- },
86
- "collection": {
87
- "type": "string",
88
- "description": "Path to collection page schema"
89
- },
90
- "cart": {
91
- "type": "string",
92
- "description": "Path to cart page schema"
93
- },
94
- "checkout": {
95
- "type": "string",
96
- "description": "Path to checkout page schema"
97
- },
98
- "landing": {
99
- "type": "string",
100
- "description": "Path to landing page schema"
101
- }
102
- },
103
- "additionalProperties": {
104
- "type": "string"
105
- },
106
- "description": "Page schema file paths"
107
- },
108
- "design_tokens": {
109
- "type": "string",
110
- "description": "Path to design tokens file (colors, fonts, spacing)"
111
- },
112
54
  "preview_image": {
113
55
  "type": "string",
114
56
  "pattern": "^.+\\.(png|jpg|jpeg|webp)$",
@@ -0,0 +1,6 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 60" width="200" height="60">
2
+ <rect width="200" height="60" fill="#f3f4f6" rx="4"/>
3
+ <text x="100" y="38" text-anchor="middle" font-family="system-ui, sans-serif" font-size="14" fill="#9ca3af">
4
+ Your Logo
5
+ </text>
6
+ </svg>
@@ -0,0 +1,150 @@
1
+ /**
2
+ * {{displayName}} - Theme JavaScript
3
+ *
4
+ * Main entry point for theme interactivity
5
+ */
6
+
7
+ (function() {
8
+ 'use strict';
9
+
10
+ /**
11
+ * Initialize theme when DOM is ready
12
+ */
13
+ document.addEventListener('DOMContentLoaded', function() {
14
+ initProductGallery();
15
+ initQuantitySelectors();
16
+ initMobileMenu();
17
+ });
18
+
19
+ /**
20
+ * Product image gallery functionality
21
+ */
22
+ function initProductGallery() {
23
+ const thumbnails = document.querySelectorAll('.thumbnail');
24
+ const mainImage = document.getElementById('main-product-image');
25
+
26
+ if (!mainImage || thumbnails.length === 0) return;
27
+
28
+ thumbnails.forEach(function(thumbnail) {
29
+ thumbnail.addEventListener('click', function() {
30
+ const newSrc = this.getAttribute('data-image');
31
+ if (newSrc) {
32
+ mainImage.src = newSrc;
33
+
34
+ // Update active state
35
+ thumbnails.forEach(t => t.classList.remove('active'));
36
+ this.classList.add('active');
37
+ }
38
+ });
39
+ });
40
+ }
41
+
42
+ /**
43
+ * Quantity selector +/- buttons
44
+ */
45
+ function initQuantitySelectors() {
46
+ const quantityInputs = document.querySelectorAll('.qty-input');
47
+
48
+ quantityInputs.forEach(function(input) {
49
+ input.addEventListener('change', function() {
50
+ const form = this.closest('form');
51
+ if (form && form.classList.contains('quantity-form')) {
52
+ form.submit();
53
+ }
54
+ });
55
+ });
56
+ }
57
+
58
+ /**
59
+ * Mobile menu toggle
60
+ */
61
+ function initMobileMenu() {
62
+ const menuToggle = document.querySelector('.mobile-menu-toggle');
63
+ const mobileMenu = document.querySelector('.mobile-menu');
64
+
65
+ if (!menuToggle || !mobileMenu) return;
66
+
67
+ menuToggle.addEventListener('click', function() {
68
+ const isOpen = mobileMenu.classList.toggle('is-open');
69
+ menuToggle.setAttribute('aria-expanded', isOpen);
70
+ document.body.classList.toggle('menu-open', isOpen);
71
+ });
72
+ }
73
+
74
+ /**
75
+ * Add to cart with AJAX (optional enhancement)
76
+ */
77
+ function initAjaxCart() {
78
+ const addToCartForms = document.querySelectorAll('.add-to-cart-form');
79
+
80
+ addToCartForms.forEach(function(form) {
81
+ form.addEventListener('submit', function(e) {
82
+ // Comment out to enable AJAX cart
83
+ // e.preventDefault();
84
+ // const formData = new FormData(form);
85
+ //
86
+ // fetch('/cart/add/', {
87
+ // method: 'POST',
88
+ // body: formData,
89
+ // headers: {
90
+ // 'X-Requested-With': 'XMLHttpRequest'
91
+ // }
92
+ // })
93
+ // .then(response => response.json())
94
+ // .then(data => {
95
+ // // Update cart count
96
+ // updateCartCount(data.item_count);
97
+ // // Show success message
98
+ // showNotification('Added to cart');
99
+ // })
100
+ // .catch(error => {
101
+ // console.error('Error:', error);
102
+ // });
103
+ });
104
+ });
105
+ }
106
+
107
+ /**
108
+ * Update cart item count in header
109
+ */
110
+ function updateCartCount(count) {
111
+ const cartCount = document.querySelector('.cart-count');
112
+ if (cartCount) {
113
+ cartCount.textContent = count;
114
+ cartCount.style.display = count > 0 ? 'flex' : 'none';
115
+ }
116
+ }
117
+
118
+ /**
119
+ * Show notification message
120
+ */
121
+ function showNotification(message, type) {
122
+ type = type || 'success';
123
+
124
+ const notification = document.createElement('div');
125
+ notification.className = 'notification notification-' + type;
126
+ notification.textContent = message;
127
+
128
+ document.body.appendChild(notification);
129
+
130
+ // Trigger animation
131
+ setTimeout(function() {
132
+ notification.classList.add('is-visible');
133
+ }, 10);
134
+
135
+ // Remove after delay
136
+ setTimeout(function() {
137
+ notification.classList.remove('is-visible');
138
+ setTimeout(function() {
139
+ notification.remove();
140
+ }, 300);
141
+ }, 3000);
142
+ }
143
+
144
+ // Expose functions globally if needed
145
+ window.ThemeHelpers = {
146
+ updateCartCount: updateCartCount,
147
+ showNotification: showNotification
148
+ };
149
+
150
+ })();
@@ -0,0 +1,22 @@
1
+ {
2
+ "settings": [
3
+ {
4
+ "id": "background_color",
5
+ "type": "color",
6
+ "label": "Background Color",
7
+ "default": "#f9fafb"
8
+ },
9
+ {
10
+ "id": "show_powered_by",
11
+ "type": "checkbox",
12
+ "label": "Show Powered by Spwig",
13
+ "default": true
14
+ },
15
+ {
16
+ "id": "show_social_links",
17
+ "type": "checkbox",
18
+ "label": "Show Social Links",
19
+ "default": true
20
+ }
21
+ ]
22
+ }
@@ -0,0 +1,80 @@
1
+ /* Footer Component Styles */
2
+
3
+ .site-footer {
4
+ background-color: var(--background-secondary, #f9fafb);
5
+ border-top: 1px solid var(--border-color, #e5e7eb);
6
+ padding: var(--spacing-xl, 3rem) 0 var(--spacing-lg, 2rem);
7
+ margin-top: auto;
8
+ }
9
+
10
+ .footer-content {
11
+ display: grid;
12
+ grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
13
+ gap: var(--spacing-lg, 2rem);
14
+ }
15
+
16
+ .footer-section h3 {
17
+ font-size: var(--heading-size-sm, 1.125rem);
18
+ font-weight: 600;
19
+ margin-bottom: var(--spacing-md, 1rem);
20
+ color: var(--text-color, #1f2937);
21
+ }
22
+
23
+ .footer-nav {
24
+ display: flex;
25
+ flex-direction: column;
26
+ gap: var(--spacing-xs, 0.5rem);
27
+ }
28
+
29
+ .footer-nav a {
30
+ color: var(--text-secondary, #6b7280);
31
+ font-size: 0.875rem;
32
+ transition: color 0.2s;
33
+ }
34
+
35
+ .footer-nav a:hover {
36
+ color: var(--primary-color, #3b82f6);
37
+ text-decoration: none;
38
+ }
39
+
40
+ .contact-info {
41
+ display: flex;
42
+ flex-direction: column;
43
+ gap: var(--spacing-xs, 0.5rem);
44
+ }
45
+
46
+ .contact-info p {
47
+ color: var(--text-secondary, #6b7280);
48
+ font-size: 0.875rem;
49
+ margin: 0;
50
+ }
51
+
52
+ .footer-bottom {
53
+ grid-column: 1 / -1;
54
+ border-top: 1px solid var(--border-color, #e5e7eb);
55
+ margin-top: var(--spacing-lg, 2rem);
56
+ padding-top: var(--spacing-lg, 2rem);
57
+ display: flex;
58
+ justify-content: space-between;
59
+ align-items: center;
60
+ flex-wrap: wrap;
61
+ gap: var(--spacing-sm, 0.75rem);
62
+ }
63
+
64
+ .footer-bottom p {
65
+ margin: 0;
66
+ font-size: 0.875rem;
67
+ color: var(--text-secondary, #6b7280);
68
+ }
69
+
70
+ /* Responsive */
71
+ @media (max-width: 768px) {
72
+ .footer-content {
73
+ grid-template-columns: 1fr;
74
+ }
75
+
76
+ .footer-bottom {
77
+ flex-direction: column;
78
+ text-align: center;
79
+ }
80
+ }
@@ -0,0 +1,104 @@
1
+ /* Header Component Styles */
2
+
3
+ .site-header {
4
+ background-color: var(--background-color, #ffffff);
5
+ border-bottom: 1px solid var(--border-color, #e5e7eb);
6
+ padding: var(--spacing-md, 1rem) 0;
7
+ position: sticky;
8
+ top: 0;
9
+ z-index: 100;
10
+ }
11
+
12
+ .header-content {
13
+ display: flex;
14
+ align-items: center;
15
+ justify-content: space-between;
16
+ gap: var(--spacing-md, 1rem);
17
+ }
18
+
19
+ .header-logo {
20
+ flex-shrink: 0;
21
+ }
22
+
23
+ .logo-image {
24
+ max-height: 48px;
25
+ width: auto;
26
+ }
27
+
28
+ .logo-text {
29
+ font-size: var(--heading-size-lg, 1.5rem);
30
+ font-weight: 700;
31
+ color: var(--text-color, #1f2937);
32
+ text-decoration: none;
33
+ }
34
+
35
+ .logo-text:hover {
36
+ text-decoration: none;
37
+ color: var(--primary-color, #3b82f6);
38
+ }
39
+
40
+ .main-navigation {
41
+ display: flex;
42
+ align-items: center;
43
+ gap: var(--spacing-md, 1rem);
44
+ }
45
+
46
+ .nav-link {
47
+ color: var(--text-color, #1f2937);
48
+ font-weight: 500;
49
+ padding: var(--spacing-xs, 0.5rem) var(--spacing-sm, 0.75rem);
50
+ border-radius: var(--border-radius, 4px);
51
+ transition: background-color 0.2s, color 0.2s;
52
+ }
53
+
54
+ .nav-link:hover {
55
+ background-color: var(--background-secondary, #f3f4f6);
56
+ color: var(--primary-color, #3b82f6);
57
+ text-decoration: none;
58
+ }
59
+
60
+ .header-actions {
61
+ display: flex;
62
+ align-items: center;
63
+ gap: var(--spacing-sm, 0.75rem);
64
+ }
65
+
66
+ .cart-link {
67
+ position: relative;
68
+ display: flex;
69
+ align-items: center;
70
+ padding: var(--spacing-xs, 0.5rem);
71
+ color: var(--text-color, #1f2937);
72
+ }
73
+
74
+ .cart-link:hover {
75
+ color: var(--primary-color, #3b82f6);
76
+ }
77
+
78
+ .cart-icon {
79
+ width: 24px;
80
+ height: 24px;
81
+ }
82
+
83
+ .cart-count {
84
+ position: absolute;
85
+ top: -4px;
86
+ right: -4px;
87
+ background-color: var(--primary-color, #3b82f6);
88
+ color: white;
89
+ font-size: 0.75rem;
90
+ font-weight: 600;
91
+ min-width: 18px;
92
+ height: 18px;
93
+ border-radius: 50%;
94
+ display: flex;
95
+ align-items: center;
96
+ justify-content: center;
97
+ }
98
+
99
+ /* Responsive */
100
+ @media (max-width: 768px) {
101
+ .main-navigation {
102
+ display: none;
103
+ }
104
+ }
File without changes
@@ -6,6 +6,6 @@
6
6
  "author": "{{author}}",
7
7
  "tier_compatibility": ["A", "B", "C"],
8
8
  "regions": ["main", "sidebar"],
9
- "category": "section",
10
- "tags": ["section", "content"]
9
+ "category": "hero",
10
+ "tags": ["hero", "banner", "content"]
11
11
  }
@@ -0,0 +1,40 @@
1
+ {
2
+ "settings": [
3
+ {
4
+ "id": "title",
5
+ "type": "text",
6
+ "label": "Title",
7
+ "default": "Welcome"
8
+ },
9
+ {
10
+ "id": "description",
11
+ "type": "textarea",
12
+ "label": "Description",
13
+ "default": "Discover our amazing products"
14
+ },
15
+ {
16
+ "id": "background_color",
17
+ "type": "color",
18
+ "label": "Background Color",
19
+ "default": "#ffffff"
20
+ },
21
+ {
22
+ "id": "show_button",
23
+ "type": "checkbox",
24
+ "label": "Show Button",
25
+ "default": true
26
+ },
27
+ {
28
+ "id": "button_text",
29
+ "type": "text",
30
+ "label": "Button Text",
31
+ "default": "Shop Now"
32
+ },
33
+ {
34
+ "id": "button_url",
35
+ "type": "url",
36
+ "label": "Button URL",
37
+ "default": "/products"
38
+ }
39
+ ]
40
+ }
@@ -0,0 +1,60 @@
1
+ /* Hero Section Component Styles */
2
+
3
+ .hero-section {
4
+ padding: var(--spacing-xxl, 5rem) 0;
5
+ background-color: var(--background-color, #ffffff);
6
+ }
7
+
8
+ .hero-content {
9
+ max-width: 800px;
10
+ margin: 0 auto;
11
+ text-align: center;
12
+ }
13
+
14
+ .hero-title {
15
+ font-family: var(--heading-font, inherit);
16
+ font-size: var(--heading-size-xxl, 3rem);
17
+ font-weight: 700;
18
+ color: var(--text-color, #1f2937);
19
+ margin-bottom: var(--spacing-md, 1rem);
20
+ line-height: 1.2;
21
+ }
22
+
23
+ .hero-description {
24
+ font-size: var(--text-lg, 1.25rem);
25
+ color: var(--text-secondary, #6b7280);
26
+ margin-bottom: var(--spacing-lg, 2rem);
27
+ line-height: 1.6;
28
+ }
29
+
30
+ .hero-button {
31
+ display: inline-block;
32
+ padding: var(--spacing-sm, 0.75rem) var(--spacing-lg, 2rem);
33
+ background-color: var(--primary-color, #3b82f6);
34
+ color: white;
35
+ font-weight: 600;
36
+ border-radius: var(--border-radius, 4px);
37
+ text-decoration: none;
38
+ transition: background-color 0.2s, transform 0.2s;
39
+ }
40
+
41
+ .hero-button:hover {
42
+ background-color: var(--primary-dark, #2563eb);
43
+ text-decoration: none;
44
+ transform: translateY(-1px);
45
+ }
46
+
47
+ /* Responsive */
48
+ @media (max-width: 768px) {
49
+ .hero-section {
50
+ padding: var(--spacing-xl, 3rem) 0;
51
+ }
52
+
53
+ .hero-title {
54
+ font-size: var(--heading-size-xl, 2rem);
55
+ }
56
+
57
+ .hero-description {
58
+ font-size: var(--text-md, 1rem);
59
+ }
60
+ }
@@ -0,0 +1,104 @@
1
+ {% extends "layout.html" %}
2
+
3
+ {% block title %}Shopping Cart - {{ store.name }}{% endblock %}
4
+
5
+ {% block body_class %}page-cart{% endblock %}
6
+
7
+ {% block content %}
8
+ <div class="cart-page">
9
+ <div class="container">
10
+ <h1 class="page-title">Shopping Cart</h1>
11
+
12
+ {% if cart.items %}
13
+ <div class="cart-layout">
14
+ <!-- Cart Items -->
15
+ <div class="cart-items">
16
+ {% for item in cart.items %}
17
+ <div class="cart-item" data-item-id="{{ item.id }}">
18
+ <div class="item-image">
19
+ {% if item.image %}
20
+ <img src="{{ item.image }}" alt="{{ item.title }}">
21
+ {% endif %}
22
+ </div>
23
+
24
+ <div class="item-details">
25
+ <h3 class="item-title">
26
+ <a href="{{ item.url }}">{{ item.title }}</a>
27
+ </h3>
28
+ {% if item.variant_title %}
29
+ <p class="item-variant">{{ item.variant_title }}</p>
30
+ {% endif %}
31
+ </div>
32
+
33
+ <div class="item-quantity">
34
+ <form action="/cart/update/" method="post" class="quantity-form">
35
+ {% csrf_token %}
36
+ <input type="hidden" name="item_id" value="{{ item.id }}">
37
+ <button type="button" class="qty-btn minus" onclick="this.nextElementSibling.stepDown(); this.form.submit();">-</button>
38
+ <input type="number" name="quantity" value="{{ item.quantity }}" min="0" max="99" class="qty-input">
39
+ <button type="button" class="qty-btn plus" onclick="this.previousElementSibling.stepUp(); this.form.submit();">+</button>
40
+ </form>
41
+ </div>
42
+
43
+ <div class="item-price">
44
+ {{ item.line_price|currency }}
45
+ </div>
46
+
47
+ <form action="/cart/remove/" method="post" class="remove-form">
48
+ {% csrf_token %}
49
+ <input type="hidden" name="item_id" value="{{ item.id }}">
50
+ <button type="submit" class="remove-btn" aria-label="Remove item">
51
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
52
+ <line x1="18" y1="6" x2="6" y2="18"></line>
53
+ <line x1="6" y1="6" x2="18" y2="18"></line>
54
+ </svg>
55
+ </button>
56
+ </form>
57
+ </div>
58
+ {% endfor %}
59
+ </div>
60
+
61
+ <!-- Cart Summary -->
62
+ <div class="cart-summary">
63
+ <h2>Order Summary</h2>
64
+
65
+ <div class="summary-line">
66
+ <span>Subtotal</span>
67
+ <span>{{ cart.subtotal|currency }}</span>
68
+ </div>
69
+
70
+ {% if cart.discount %}
71
+ <div class="summary-line discount">
72
+ <span>Discount</span>
73
+ <span>-{{ cart.discount|currency }}</span>
74
+ </div>
75
+ {% endif %}
76
+
77
+ <div class="summary-line shipping">
78
+ <span>Shipping</span>
79
+ <span>Calculated at checkout</span>
80
+ </div>
81
+
82
+ <div class="summary-total">
83
+ <span>Total</span>
84
+ <span>{{ cart.total|currency }}</span>
85
+ </div>
86
+
87
+ <a href="/checkout/" class="checkout-button">
88
+ Proceed to Checkout
89
+ </a>
90
+
91
+ <a href="/" class="continue-shopping">
92
+ Continue Shopping
93
+ </a>
94
+ </div>
95
+ </div>
96
+ {% else %}
97
+ <div class="cart-empty">
98
+ <p>Your cart is empty.</p>
99
+ <a href="/" class="continue-shopping-button">Continue Shopping</a>
100
+ </div>
101
+ {% endif %}
102
+ </div>
103
+ </div>
104
+ {% endblock %}