v1.0.0 - Initial backup
@@ -0,0 +1,543 @@
|
||||
/**
|
||||
* Tom's Java Jive - Admin Panel Styles
|
||||
*/
|
||||
|
||||
:root {
|
||||
/* Admin Colors */
|
||||
--admin-sidebar-bg: #1F2937;
|
||||
--admin-sidebar-hover: #374151;
|
||||
--admin-sidebar-active: var(--color-primary);
|
||||
--admin-header-bg: #FFFFFF;
|
||||
--admin-content-bg: #F9FAFB;
|
||||
--admin-text-light: #9CA3AF;
|
||||
}
|
||||
|
||||
/* ================================
|
||||
Admin Layout
|
||||
================================ */
|
||||
.admin-layout {
|
||||
display: flex;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.admin-sidebar {
|
||||
width: 260px;
|
||||
background: var(--admin-sidebar-bg);
|
||||
color: white;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
z-index: 100;
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
|
||||
.admin-sidebar-header {
|
||||
padding: 1.5rem;
|
||||
border-bottom: 1px solid rgba(255,255,255,0.1);
|
||||
}
|
||||
|
||||
.admin-logo {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
color: white;
|
||||
font-family: var(--font-heading);
|
||||
font-size: 1.25rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.admin-logo img {
|
||||
height: 32px;
|
||||
}
|
||||
|
||||
.admin-nav {
|
||||
flex: 1;
|
||||
padding: 1rem 0;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.admin-nav-section {
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.admin-nav-title {
|
||||
padding: 0.5rem 1.5rem;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
color: var(--admin-text-light);
|
||||
}
|
||||
|
||||
.admin-nav-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
padding: 0.75rem 1.5rem;
|
||||
color: rgba(255,255,255,0.8);
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.admin-nav-item:hover {
|
||||
background: var(--admin-sidebar-hover);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.admin-nav-item.active {
|
||||
background: var(--admin-sidebar-active);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.admin-nav-item i {
|
||||
width: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.admin-nav-badge {
|
||||
margin-left: auto;
|
||||
padding: 0.125rem 0.5rem;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
background: var(--color-error);
|
||||
color: white;
|
||||
border-radius: 9999px;
|
||||
}
|
||||
|
||||
.admin-main {
|
||||
flex: 1;
|
||||
margin-left: 260px;
|
||||
background: var(--admin-content-bg);
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.admin-header {
|
||||
background: var(--admin-header-bg);
|
||||
padding: 1rem 1.5rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
border-bottom: 1px solid var(--color-border);
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 50;
|
||||
}
|
||||
|
||||
.admin-header-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.admin-search {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
padding: 0.5rem 1rem;
|
||||
background: var(--color-background);
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--radius-md);
|
||||
min-width: 300px;
|
||||
}
|
||||
|
||||
.admin-search input {
|
||||
border: none;
|
||||
background: transparent;
|
||||
outline: none;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.admin-header-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.admin-user {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
padding: 0.5rem;
|
||||
border-radius: var(--radius-md);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.admin-user:hover {
|
||||
background: var(--color-background);
|
||||
}
|
||||
|
||||
.admin-avatar {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 50%;
|
||||
background: var(--color-primary);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: white;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.admin-content {
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
/* ================================
|
||||
Admin Cards
|
||||
================================ */
|
||||
.admin-card {
|
||||
background: var(--color-surface);
|
||||
border-radius: var(--radius-lg);
|
||||
border: 1px solid var(--color-border);
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.admin-card-header {
|
||||
padding: 1rem 1.5rem;
|
||||
border-bottom: 1px solid var(--color-border);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.admin-card-title {
|
||||
font-size: 1rem;
|
||||
font-weight: 600;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.admin-card-body {
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
/* ================================
|
||||
Stats Cards
|
||||
================================ */
|
||||
.stats-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||
gap: 1.5rem;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.stat-card {
|
||||
background: var(--color-surface);
|
||||
border-radius: var(--radius-lg);
|
||||
border: 1px solid var(--color-border);
|
||||
padding: 1.5rem;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.stat-card-icon {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
border-radius: var(--radius-md);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
|
||||
.stat-card-icon.primary {
|
||||
background: rgba(232, 106, 51, 0.1);
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
.stat-card-icon.success {
|
||||
background: rgba(16, 185, 129, 0.1);
|
||||
color: var(--color-success);
|
||||
}
|
||||
|
||||
.stat-card-icon.warning {
|
||||
background: rgba(245, 158, 11, 0.1);
|
||||
color: var(--color-warning);
|
||||
}
|
||||
|
||||
.stat-card-icon.error {
|
||||
background: rgba(239, 68, 68, 0.1);
|
||||
color: var(--color-error);
|
||||
}
|
||||
|
||||
.stat-card-value {
|
||||
font-size: 1.5rem;
|
||||
font-weight: 700;
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
.stat-card-label {
|
||||
font-size: 0.875rem;
|
||||
color: var(--color-text-muted);
|
||||
}
|
||||
|
||||
/* ================================
|
||||
Admin Tables
|
||||
================================ */
|
||||
.admin-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.admin-table th,
|
||||
.admin-table td {
|
||||
padding: 0.875rem 1rem;
|
||||
text-align: left;
|
||||
border-bottom: 1px solid var(--color-border);
|
||||
}
|
||||
|
||||
.admin-table th {
|
||||
font-weight: 600;
|
||||
font-size: 0.875rem;
|
||||
color: var(--color-text-muted);
|
||||
background: var(--color-background);
|
||||
}
|
||||
|
||||
.admin-table tbody tr:hover {
|
||||
background: var(--color-background);
|
||||
}
|
||||
|
||||
.admin-table td a {
|
||||
color: var(--color-primary);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* ================================
|
||||
Page Header
|
||||
================================ */
|
||||
.page-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.page-title {
|
||||
font-size: 1.5rem;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* ================================
|
||||
Form Improvements for Admin
|
||||
================================ */
|
||||
.admin-form-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
.admin-form-grid .full-width {
|
||||
grid-column: 1 / -1;
|
||||
}
|
||||
|
||||
/* ================================
|
||||
Responsive Admin
|
||||
================================ */
|
||||
.sidebar-toggle {
|
||||
display: none;
|
||||
background: none;
|
||||
border: none;
|
||||
font-size: 1.5rem;
|
||||
cursor: pointer;
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
@media (max-width: 1024px) {
|
||||
.admin-sidebar {
|
||||
transform: translateX(-100%);
|
||||
}
|
||||
|
||||
.admin-sidebar.open {
|
||||
transform: translateX(0);
|
||||
}
|
||||
|
||||
.admin-main {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.sidebar-toggle {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.admin-search {
|
||||
min-width: 200px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.stats-grid {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
|
||||
.admin-form-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.admin-table {
|
||||
display: block;
|
||||
overflow-x: auto;
|
||||
}
|
||||
}
|
||||
|
||||
/* ================================
|
||||
POS Specific Styles
|
||||
================================ */
|
||||
.pos-layout {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 400px;
|
||||
gap: 1.5rem;
|
||||
height: calc(100vh - 140px);
|
||||
}
|
||||
|
||||
.pos-products {
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.pos-cart {
|
||||
background: var(--color-surface);
|
||||
border-radius: var(--radius-lg);
|
||||
border: 1px solid var(--color-border);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.pos-cart-header {
|
||||
padding: 1rem;
|
||||
border-bottom: 1px solid var(--color-border);
|
||||
}
|
||||
|
||||
.pos-cart-items {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.pos-cart-item {
|
||||
display: flex;
|
||||
gap: 0.75rem;
|
||||
padding: 0.75rem 0;
|
||||
border-bottom: 1px solid var(--color-border);
|
||||
}
|
||||
|
||||
.pos-cart-footer {
|
||||
padding: 1rem;
|
||||
border-top: 1px solid var(--color-border);
|
||||
background: var(--color-background);
|
||||
}
|
||||
|
||||
.pos-totals {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.pos-total-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.pos-total-row.grand-total {
|
||||
font-size: 1.25rem;
|
||||
font-weight: 700;
|
||||
padding-top: 0.5rem;
|
||||
border-top: 1px solid var(--color-border);
|
||||
}
|
||||
|
||||
.pos-product-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.pos-product-card {
|
||||
background: var(--color-surface);
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--radius-md);
|
||||
padding: 0.75rem;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.pos-product-card:hover {
|
||||
border-color: var(--color-primary);
|
||||
box-shadow: var(--shadow-md);
|
||||
}
|
||||
|
||||
.pos-product-card img {
|
||||
width: 100%;
|
||||
aspect-ratio: 1;
|
||||
object-fit: cover;
|
||||
border-radius: var(--radius-sm);
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.pos-product-name {
|
||||
font-size: 0.875rem;
|
||||
font-weight: 500;
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
|
||||
.pos-product-price {
|
||||
font-size: 0.875rem;
|
||||
color: var(--color-primary);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* Payment Buttons */
|
||||
.payment-methods {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 0.75rem;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.payment-btn {
|
||||
padding: 1rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
border: 2px solid var(--color-border);
|
||||
background: var(--color-surface);
|
||||
border-radius: var(--radius-md);
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.payment-btn:hover {
|
||||
border-color: var(--color-primary);
|
||||
}
|
||||
|
||||
.payment-btn.selected {
|
||||
border-color: var(--color-primary);
|
||||
background: rgba(232, 106, 51, 0.05);
|
||||
}
|
||||
|
||||
.payment-btn i {
|
||||
font-size: 1.5rem;
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
@media (max-width: 1024px) {
|
||||
.pos-layout {
|
||||
grid-template-columns: 1fr;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.pos-cart {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 50vh;
|
||||
transform: translateY(calc(100% - 60px));
|
||||
transition: transform 0.3s ease;
|
||||
z-index: 100;
|
||||
border-radius: var(--radius-lg) var(--radius-lg) 0 0;
|
||||
}
|
||||
|
||||
.pos-cart.expanded {
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,789 @@
|
||||
/**
|
||||
* Tom's Java Jive - Main Stylesheet
|
||||
* Matching original React design
|
||||
*/
|
||||
|
||||
:root {
|
||||
--color-primary: #FF5E1A;
|
||||
--color-primary-dark: #E54D0D;
|
||||
--color-secondary: #8B4513;
|
||||
--color-accent: #FF5E1A;
|
||||
--color-background: #FDFBF7;
|
||||
--color-surface: #FFFFFF;
|
||||
--color-text: #1B1B1B;
|
||||
--color-text-muted: #6B7280;
|
||||
--color-text-light: #9CA3AF;
|
||||
--color-border: #E8E2D9;
|
||||
--color-success: #10B981;
|
||||
--color-warning: #F59E0B;
|
||||
--color-error: #EF4444;
|
||||
|
||||
--font-primary: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||
--font-display: 'Playfair Display', Georgia, serif;
|
||||
|
||||
--radius-sm: 6px;
|
||||
--radius-md: 8px;
|
||||
--radius-lg: 12px;
|
||||
--radius-xl: 16px;
|
||||
--radius-full: 9999px;
|
||||
|
||||
--shadow-sm: 0 1px 2px rgba(0,0,0,0.05);
|
||||
--shadow-md: 0 4px 6px -1px rgba(0,0,0,0.1);
|
||||
--shadow-lg: 0 10px 15px -3px rgba(0,0,0,0.1);
|
||||
--shadow-xl: 0 20px 25px -5px rgba(0,0,0,0.1);
|
||||
|
||||
--transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
html {
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: var(--font-primary);
|
||||
font-size: 16px;
|
||||
line-height: 1.6;
|
||||
color: var(--color-text);
|
||||
background: var(--color-background);
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--color-primary);
|
||||
text-decoration: none;
|
||||
transition: var(--transition);
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: var(--color-primary-dark);
|
||||
}
|
||||
|
||||
img {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
/* Container */
|
||||
.container {
|
||||
width: 100%;
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 0 1.5rem;
|
||||
}
|
||||
|
||||
/* Header / Navigation */
|
||||
.header {
|
||||
background: var(--color-surface);
|
||||
border-bottom: 1px solid var(--color-border);
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
.nav {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
height: 70px;
|
||||
}
|
||||
|
||||
.logo {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
font-weight: 700;
|
||||
font-size: 1.25rem;
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
.logo:hover {
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
.logo-img {
|
||||
height: 45px;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.logo-text {
|
||||
font-family: var(--font-display);
|
||||
font-size: 1.5rem;
|
||||
color: var(--color-secondary);
|
||||
}
|
||||
|
||||
.nav-links {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 2rem;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.nav-links a {
|
||||
color: var(--color-text);
|
||||
font-weight: 500;
|
||||
font-size: 0.9375rem;
|
||||
padding: 0.5rem 0;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.nav-links a:hover {
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
.nav-links a.active {
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
.nav-links a::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 0;
|
||||
height: 2px;
|
||||
background: var(--color-primary);
|
||||
transition: width 0.3s ease;
|
||||
}
|
||||
|
||||
.nav-links a:hover::after,
|
||||
.nav-links a.active::after {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.nav-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.cart-btn {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
border-radius: var(--radius-full);
|
||||
background: var(--color-background);
|
||||
color: var(--color-text);
|
||||
font-size: 1.25rem;
|
||||
transition: var(--transition);
|
||||
}
|
||||
|
||||
.cart-btn:hover {
|
||||
background: var(--color-primary);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.cart-count {
|
||||
position: absolute;
|
||||
top: -4px;
|
||||
right: -4px;
|
||||
background: var(--color-primary);
|
||||
color: white;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
min-width: 20px;
|
||||
height: 20px;
|
||||
border-radius: var(--radius-full);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
/* Hero Section */
|
||||
.hero {
|
||||
background: linear-gradient(135deg, rgba(27,27,27,0.7) 0%, rgba(27,27,27,0.4) 100%),
|
||||
url('/assets/images/hero-coffee.jpg');
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
min-height: 600px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
color: white;
|
||||
padding: 4rem 0;
|
||||
}
|
||||
|
||||
.hero h1 {
|
||||
font-family: var(--font-display);
|
||||
font-size: 3.5rem;
|
||||
font-weight: 700;
|
||||
margin-bottom: 1rem;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.hero p {
|
||||
font-size: 1.25rem;
|
||||
margin-bottom: 2rem;
|
||||
opacity: 0.9;
|
||||
max-width: 600px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
/* Buttons */
|
||||
.btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 0.5rem;
|
||||
padding: 0.75rem 1.5rem;
|
||||
font-size: 1rem;
|
||||
font-weight: 600;
|
||||
border-radius: var(--radius-md);
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
transition: var(--transition);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background: var(--color-primary);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
background: var(--color-primary-dark);
|
||||
color: white;
|
||||
transform: translateY(-2px);
|
||||
box-shadow: var(--shadow-lg);
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
background: white;
|
||||
color: var(--color-text);
|
||||
border: 2px solid var(--color-border);
|
||||
}
|
||||
|
||||
.btn-secondary:hover {
|
||||
border-color: var(--color-primary);
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
.btn-outline {
|
||||
background: transparent;
|
||||
color: white;
|
||||
border: 2px solid white;
|
||||
}
|
||||
|
||||
.btn-outline:hover {
|
||||
background: white;
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
.btn-lg {
|
||||
padding: 1rem 2rem;
|
||||
font-size: 1.125rem;
|
||||
}
|
||||
|
||||
.btn-sm {
|
||||
padding: 0.5rem 1rem;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.btn-block {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* Sections */
|
||||
.section {
|
||||
padding: 5rem 0;
|
||||
}
|
||||
|
||||
.section-header {
|
||||
text-align: center;
|
||||
margin-bottom: 3rem;
|
||||
}
|
||||
|
||||
.section-header h2 {
|
||||
font-family: var(--font-display);
|
||||
font-size: 2.5rem;
|
||||
font-weight: 700;
|
||||
margin-bottom: 0.5rem;
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
.section-header p {
|
||||
font-size: 1.125rem;
|
||||
color: var(--color-text-muted);
|
||||
}
|
||||
|
||||
/* Product Grid */
|
||||
.product-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
|
||||
gap: 2rem;
|
||||
}
|
||||
|
||||
/* Product Card */
|
||||
.product-card {
|
||||
background: var(--color-surface);
|
||||
border-radius: var(--radius-lg);
|
||||
overflow: hidden;
|
||||
transition: var(--transition);
|
||||
box-shadow: var(--shadow-sm);
|
||||
}
|
||||
|
||||
.product-card:hover {
|
||||
transform: translateY(-4px);
|
||||
box-shadow: var(--shadow-xl);
|
||||
}
|
||||
|
||||
.product-card-image {
|
||||
position: relative;
|
||||
padding-top: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.product-card-image img {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
transition: transform 0.5s ease;
|
||||
}
|
||||
|
||||
.product-card:hover .product-card-image img {
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
.product-card-badge {
|
||||
position: absolute;
|
||||
top: 1rem;
|
||||
left: 1rem;
|
||||
background: var(--color-primary);
|
||||
color: white;
|
||||
padding: 0.25rem 0.75rem;
|
||||
border-radius: var(--radius-full);
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.product-card-body {
|
||||
padding: 1.25rem;
|
||||
}
|
||||
|
||||
.product-card-category {
|
||||
font-size: 0.8125rem;
|
||||
color: var(--color-primary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.product-card-title {
|
||||
font-size: 1.125rem;
|
||||
font-weight: 600;
|
||||
margin-bottom: 0.5rem;
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
.product-card-title a {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.product-card-title a:hover {
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
.product-card-price {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.product-card-price .current {
|
||||
font-size: 1.25rem;
|
||||
font-weight: 700;
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
.product-card-price .original {
|
||||
font-size: 0.9375rem;
|
||||
color: var(--color-text-muted);
|
||||
text-decoration: line-through;
|
||||
}
|
||||
|
||||
.product-card-rating {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.25rem;
|
||||
color: #F59E0B;
|
||||
font-size: 0.875rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.product-card-rating span {
|
||||
color: var(--color-text-muted);
|
||||
margin-left: 0.25rem;
|
||||
}
|
||||
|
||||
/* Features Section */
|
||||
.features-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
||||
gap: 2rem;
|
||||
}
|
||||
|
||||
.feature-card {
|
||||
text-align: center;
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
.feature-icon {
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
background: linear-gradient(135deg, var(--color-primary), var(--color-primary-dark));
|
||||
border-radius: var(--radius-lg);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin: 0 auto 1.5rem;
|
||||
color: white;
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.feature-card h3 {
|
||||
font-size: 1.25rem;
|
||||
font-weight: 600;
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.feature-card p {
|
||||
color: var(--color-text-muted);
|
||||
font-size: 0.9375rem;
|
||||
}
|
||||
|
||||
/* Cards */
|
||||
.card {
|
||||
background: var(--color-surface);
|
||||
border-radius: var(--radius-lg);
|
||||
box-shadow: var(--shadow-sm);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
padding: 1.25rem;
|
||||
border-bottom: 1px solid var(--color-border);
|
||||
}
|
||||
|
||||
.card-body {
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
/* Forms */
|
||||
.form-group {
|
||||
margin-bottom: 1.25rem;
|
||||
}
|
||||
|
||||
.form-label {
|
||||
display: block;
|
||||
font-size: 0.875rem;
|
||||
font-weight: 500;
|
||||
margin-bottom: 0.5rem;
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
.form-input,
|
||||
.form-select,
|
||||
.form-textarea {
|
||||
width: 100%;
|
||||
padding: 0.75rem 1rem;
|
||||
font-size: 1rem;
|
||||
border: 2px solid var(--color-border);
|
||||
border-radius: var(--radius-md);
|
||||
background: var(--color-surface);
|
||||
transition: var(--transition);
|
||||
font-family: inherit;
|
||||
}
|
||||
|
||||
.form-input:focus,
|
||||
.form-select:focus,
|
||||
.form-textarea:focus {
|
||||
outline: none;
|
||||
border-color: var(--color-primary);
|
||||
box-shadow: 0 0 0 3px rgba(255,94,26,0.1);
|
||||
}
|
||||
|
||||
.form-textarea {
|
||||
min-height: 120px;
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
.form-error {
|
||||
color: var(--color-error);
|
||||
font-size: 0.8125rem;
|
||||
margin-top: 0.25rem;
|
||||
}
|
||||
|
||||
.form-checkbox {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* Alerts */
|
||||
.alert {
|
||||
padding: 1rem 1.25rem;
|
||||
border-radius: var(--radius-md);
|
||||
margin-bottom: 1rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.alert-success {
|
||||
background: rgba(16, 185, 129, 0.1);
|
||||
color: var(--color-success);
|
||||
border: 1px solid rgba(16, 185, 129, 0.3);
|
||||
}
|
||||
|
||||
.alert-error {
|
||||
background: rgba(239, 68, 68, 0.1);
|
||||
color: var(--color-error);
|
||||
border: 1px solid rgba(239, 68, 68, 0.3);
|
||||
}
|
||||
|
||||
.alert-warning {
|
||||
background: rgba(245, 158, 11, 0.1);
|
||||
color: var(--color-warning);
|
||||
border: 1px solid rgba(245, 158, 11, 0.3);
|
||||
}
|
||||
|
||||
/* Badges */
|
||||
.badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: 0.25rem 0.75rem;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
border-radius: var(--radius-full);
|
||||
}
|
||||
|
||||
.badge-primary {
|
||||
background: rgba(255,94,26,0.15);
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
.badge-success {
|
||||
background: rgba(16, 185, 129, 0.15);
|
||||
color: var(--color-success);
|
||||
}
|
||||
|
||||
.badge-warning {
|
||||
background: rgba(245, 158, 11, 0.15);
|
||||
color: var(--color-warning);
|
||||
}
|
||||
|
||||
.badge-error {
|
||||
background: rgba(239, 68, 68, 0.15);
|
||||
color: var(--color-error);
|
||||
}
|
||||
|
||||
/* Newsletter Section */
|
||||
.newsletter {
|
||||
background: linear-gradient(135deg, var(--color-secondary) 0%, #5D2E0A 100%);
|
||||
color: white;
|
||||
padding: 4rem 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.newsletter h2 {
|
||||
font-family: var(--font-display);
|
||||
font-size: 2rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.newsletter p {
|
||||
opacity: 0.9;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.newsletter-form {
|
||||
display: flex;
|
||||
gap: 0.75rem;
|
||||
max-width: 450px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.newsletter-form input {
|
||||
flex: 1;
|
||||
padding: 0.875rem 1rem;
|
||||
border: none;
|
||||
border-radius: var(--radius-md);
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.newsletter-form button {
|
||||
padding: 0.875rem 1.5rem;
|
||||
background: var(--color-primary);
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: var(--radius-md);
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: var(--transition);
|
||||
}
|
||||
|
||||
.newsletter-form button:hover {
|
||||
background: var(--color-primary-dark);
|
||||
}
|
||||
|
||||
/* Footer */
|
||||
.footer {
|
||||
background: var(--color-text);
|
||||
color: white;
|
||||
padding: 4rem 0 2rem;
|
||||
}
|
||||
|
||||
.footer-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 2fr 1fr 1fr 1fr;
|
||||
gap: 3rem;
|
||||
margin-bottom: 3rem;
|
||||
}
|
||||
|
||||
.footer-brand p {
|
||||
color: rgba(255,255,255,0.7);
|
||||
margin-top: 1rem;
|
||||
font-size: 0.9375rem;
|
||||
}
|
||||
|
||||
.footer h4 {
|
||||
font-size: 1rem;
|
||||
font-weight: 600;
|
||||
margin-bottom: 1.25rem;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.footer-links {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.footer-links li {
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.footer-links a {
|
||||
color: rgba(255,255,255,0.7);
|
||||
font-size: 0.9375rem;
|
||||
transition: var(--transition);
|
||||
}
|
||||
|
||||
.footer-links a:hover {
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
.footer-bottom {
|
||||
padding-top: 2rem;
|
||||
border-top: 1px solid rgba(255,255,255,0.1);
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
font-size: 0.875rem;
|
||||
color: rgba(255,255,255,0.5);
|
||||
}
|
||||
|
||||
.footer-social {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.footer-social a {
|
||||
color: rgba(255,255,255,0.7);
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
|
||||
.footer-social a:hover {
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
/* Utilities */
|
||||
.text-center { text-align: center; }
|
||||
.text-muted { color: var(--color-text-muted); }
|
||||
.text-success { color: var(--color-success); }
|
||||
.text-error { color: var(--color-error); }
|
||||
|
||||
.mb-0 { margin-bottom: 0; }
|
||||
.mb-1 { margin-bottom: 1rem; }
|
||||
.mb-2 { margin-bottom: 1.5rem; }
|
||||
.mt-1 { margin-top: 1rem; }
|
||||
.mt-2 { margin-top: 1.5rem; }
|
||||
|
||||
/* Loading Spinner */
|
||||
.loading {
|
||||
display: inline-block;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border: 2px solid currentColor;
|
||||
border-radius: 50%;
|
||||
border-top-color: transparent;
|
||||
animation: spin 0.8s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
/* Responsive */
|
||||
@media (max-width: 1024px) {
|
||||
.footer-grid {
|
||||
grid-template-columns: 1fr 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.hero h1 {
|
||||
font-size: 2.5rem;
|
||||
}
|
||||
|
||||
.hero p {
|
||||
font-size: 1.125rem;
|
||||
}
|
||||
|
||||
.section-header h2 {
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
.nav-links {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.footer-grid {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 2rem;
|
||||
}
|
||||
|
||||
.footer-bottom {
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.newsletter-form {
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.hero h1 {
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
.product-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="72" height="72" viewBox="0 0 72 72" xmlns="http://www.w3.org/2000/svg">
|
||||
<circle cx="36" cy="36" r="36" fill="#FF5E1A"/>
|
||||
<text x="36" y="45" font-family="Arial, sans-serif" font-size="28" font-weight="bold" fill="white" text-anchor="middle">TJ</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 311 B |
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="128" height="128" viewBox="0 0 128 128" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="128" height="128" rx="19" fill="#FF5E1A"/>
|
||||
<g transform="translate(12, 12) scale(1.28)">
|
||||
<path fill="#FFF8F0" d="M60 20H20c-2.2 0-4 1.8-4 4v44c0 6.6 5.4 12 12 12h24c6.6 0 12-5.4 12-12V24c0-2.2-1.8-4-4-4zm-8 4v4H28v-4h24zm8 44c0 4.4-3.6 8-8 8H28c-4.4 0-8-3.6-8-8V32h40v36z"/>
|
||||
<path fill="#FFF8F0" d="M72 32h-4v8h4c2.2 0 4 1.8 4 4v8c0 2.2-1.8 4-4 4h-4v8h4c6.6 0 12-5.4 12-12v-8c0-6.6-5.4-12-12-12z"/>
|
||||
<path fill="#FFF8F0" opacity="0.6" d="M32 12c0-2.2 1.8-4 4-4s4 1.8 4 4v4h-8v-4zm12 0c0-2.2 1.8-4 4-4s4 1.8 4 4v4h-8v-4zm12 0c0-2.2 1.8-4 4-4s4 1.8 4 4v4h-8v-4z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 731 B |
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="144" height="144" viewBox="0 0 144 144" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="144" height="144" rx="21" fill="#FF5E1A"/>
|
||||
<g transform="translate(14, 14) scale(1.44)">
|
||||
<path fill="#FFF8F0" d="M60 20H20c-2.2 0-4 1.8-4 4v44c0 6.6 5.4 12 12 12h24c6.6 0 12-5.4 12-12V24c0-2.2-1.8-4-4-4zm-8 4v4H28v-4h24zm8 44c0 4.4-3.6 8-8 8H28c-4.4 0-8-3.6-8-8V32h40v36z"/>
|
||||
<path fill="#FFF8F0" d="M72 32h-4v8h4c2.2 0 4 1.8 4 4v8c0 2.2-1.8 4-4 4h-4v8h4c6.6 0 12-5.4 12-12v-8c0-6.6-5.4-12-12-12z"/>
|
||||
<path fill="#FFF8F0" opacity="0.6" d="M32 12c0-2.2 1.8-4 4-4s4 1.8 4 4v4h-8v-4zm12 0c0-2.2 1.8-4 4-4s4 1.8 4 4v4h-8v-4zm12 0c0-2.2 1.8-4 4-4s4 1.8 4 4v4h-8v-4z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 731 B |
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="152" height="152" viewBox="0 0 152 152" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="152" height="152" rx="22" fill="#FF5E1A"/>
|
||||
<g transform="translate(15, 15) scale(1.52)">
|
||||
<path fill="#FFF8F0" d="M60 20H20c-2.2 0-4 1.8-4 4v44c0 6.6 5.4 12 12 12h24c6.6 0 12-5.4 12-12V24c0-2.2-1.8-4-4-4zm-8 4v4H28v-4h24zm8 44c0 4.4-3.6 8-8 8H28c-4.4 0-8-3.6-8-8V32h40v36z"/>
|
||||
<path fill="#FFF8F0" d="M72 32h-4v8h4c2.2 0 4 1.8 4 4v8c0 2.2-1.8 4-4 4h-4v8h4c6.6 0 12-5.4 12-12v-8c0-6.6-5.4-12-12-12z"/>
|
||||
<path fill="#FFF8F0" opacity="0.6" d="M32 12c0-2.2 1.8-4 4-4s4 1.8 4 4v4h-8v-4zm12 0c0-2.2 1.8-4 4-4s4 1.8 4 4v4h-8v-4zm12 0c0-2.2 1.8-4 4-4s4 1.8 4 4v4h-8v-4z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 731 B |
@@ -0,0 +1,8 @@
|
||||
<svg width="192" height="192" viewBox="0 0 192 192" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="192" height="192" rx="28" fill="#FF5E1A"/>
|
||||
<g transform="translate(19, 19) scale(1.54)">
|
||||
<path fill="#FFF8F0" d="M60 20H20c-2.2 0-4 1.8-4 4v44c0 6.6 5.4 12 12 12h24c6.6 0 12-5.4 12-12V24c0-2.2-1.8-4-4-4zm-8 4v4H28v-4h24zm8 44c0 4.4-3.6 8-8 8H28c-4.4 0-8-3.6-8-8V32h40v36z"/>
|
||||
<path fill="#FFF8F0" d="M72 32h-4v8h4c2.2 0 4 1.8 4 4v8c0 2.2-1.8 4-4 4h-4v8h4c6.6 0 12-5.4 12-12v-8c0-6.6-5.4-12-12-12z"/>
|
||||
<path fill="#FFF8F0" opacity="0.6" d="M32 12c0-2.2 1.8-4 4-4s4 1.8 4 4v4h-8v-4zm12 0c0-2.2 1.8-4 4-4s4 1.8 4 4v4h-8v-4zm12 0c0-2.2 1.8-4 4-4s4 1.8 4 4v4h-8v-4z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 692 B |
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="192" height="192" viewBox="0 0 192 192" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="192" height="192" rx="28" fill="#FF5E1A"/>
|
||||
<g transform="translate(19, 19) scale(1.92)">
|
||||
<path fill="#FFF8F0" d="M60 20H20c-2.2 0-4 1.8-4 4v44c0 6.6 5.4 12 12 12h24c6.6 0 12-5.4 12-12V24c0-2.2-1.8-4-4-4zm-8 4v4H28v-4h24zm8 44c0 4.4-3.6 8-8 8H28c-4.4 0-8-3.6-8-8V32h40v36z"/>
|
||||
<path fill="#FFF8F0" d="M72 32h-4v8h4c2.2 0 4 1.8 4 4v8c0 2.2-1.8 4-4 4h-4v8h4c6.6 0 12-5.4 12-12v-8c0-6.6-5.4-12-12-12z"/>
|
||||
<path fill="#FFF8F0" opacity="0.6" d="M32 12c0-2.2 1.8-4 4-4s4 1.8 4 4v4h-8v-4zm12 0c0-2.2 1.8-4 4-4s4 1.8 4 4v4h-8v-4zm12 0c0-2.2 1.8-4 4-4s4 1.8 4 4v4h-8v-4z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 731 B |
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="384" height="384" viewBox="0 0 384 384" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="384" height="384" rx="57" fill="#FF5E1A"/>
|
||||
<g transform="translate(38, 38) scale(3.84)">
|
||||
<path fill="#FFF8F0" d="M60 20H20c-2.2 0-4 1.8-4 4v44c0 6.6 5.4 12 12 12h24c6.6 0 12-5.4 12-12V24c0-2.2-1.8-4-4-4zm-8 4v4H28v-4h24zm8 44c0 4.4-3.6 8-8 8H28c-4.4 0-8-3.6-8-8V32h40v36z"/>
|
||||
<path fill="#FFF8F0" d="M72 32h-4v8h4c2.2 0 4 1.8 4 4v8c0 2.2-1.8 4-4 4h-4v8h4c6.6 0 12-5.4 12-12v-8c0-6.6-5.4-12-12-12z"/>
|
||||
<path fill="#FFF8F0" opacity="0.6" d="M32 12c0-2.2 1.8-4 4-4s4 1.8 4 4v4h-8v-4zm12 0c0-2.2 1.8-4 4-4s4 1.8 4 4v4h-8v-4zm12 0c0-2.2 1.8-4 4-4s4 1.8 4 4v4h-8v-4z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 731 B |
@@ -0,0 +1,8 @@
|
||||
<svg width="512" height="512" viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="512" height="512" rx="76" fill="#FF5E1A"/>
|
||||
<g transform="translate(51, 51) scale(4.1)">
|
||||
<path fill="#FFF8F0" d="M60 20H20c-2.2 0-4 1.8-4 4v44c0 6.6 5.4 12 12 12h24c6.6 0 12-5.4 12-12V24c0-2.2-1.8-4-4-4zm-8 4v4H28v-4h24zm8 44c0 4.4-3.6 8-8 8H28c-4.4 0-8-3.6-8-8V32h40v36z"/>
|
||||
<path fill="#FFF8F0" d="M72 32h-4v8h4c2.2 0 4 1.8 4 4v8c0 2.2-1.8 4-4 4h-4v8h4c6.6 0 12-5.4 12-12v-8c0-6.6-5.4-12-12-12z"/>
|
||||
<path fill="#FFF8F0" opacity="0.6" d="M32 12c0-2.2 1.8-4 4-4s4 1.8 4 4v4h-8v-4zm12 0c0-2.2 1.8-4 4-4s4 1.8 4 4v4h-8v-4zm12 0c0-2.2 1.8-4 4-4s4 1.8 4 4v4h-8v-4z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 691 B |
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="512" height="512" viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="512" height="512" rx="76" fill="#FF5E1A"/>
|
||||
<g transform="translate(51, 51) scale(5.12)">
|
||||
<path fill="#FFF8F0" d="M60 20H20c-2.2 0-4 1.8-4 4v44c0 6.6 5.4 12 12 12h24c6.6 0 12-5.4 12-12V24c0-2.2-1.8-4-4-4zm-8 4v4H28v-4h24zm8 44c0 4.4-3.6 8-8 8H28c-4.4 0-8-3.6-8-8V32h40v36z"/>
|
||||
<path fill="#FFF8F0" d="M72 32h-4v8h4c2.2 0 4 1.8 4 4v8c0 2.2-1.8 4-4 4h-4v8h4c6.6 0 12-5.4 12-12v-8c0-6.6-5.4-12-12-12z"/>
|
||||
<path fill="#FFF8F0" opacity="0.6" d="M32 12c0-2.2 1.8-4 4-4s4 1.8 4 4v4h-8v-4zm12 0c0-2.2 1.8-4 4-4s4 1.8 4 4v4h-8v-4zm12 0c0-2.2 1.8-4 4-4s4 1.8 4 4v4h-8v-4z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 731 B |
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="72" height="72" viewBox="0 0 72 72" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="72" height="72" rx="10" fill="#FF5E1A"/>
|
||||
<g transform="translate(7, 7) scale(0.72)">
|
||||
<path fill="#FFF8F0" d="M60 20H20c-2.2 0-4 1.8-4 4v44c0 6.6 5.4 12 12 12h24c6.6 0 12-5.4 12-12V24c0-2.2-1.8-4-4-4zm-8 4v4H28v-4h24zm8 44c0 4.4-3.6 8-8 8H28c-4.4 0-8-3.6-8-8V32h40v36z"/>
|
||||
<path fill="#FFF8F0" d="M72 32h-4v8h4c2.2 0 4 1.8 4 4v8c0 2.2-1.8 4-4 4h-4v8h4c6.6 0 12-5.4 12-12v-8c0-6.6-5.4-12-12-12z"/>
|
||||
<path fill="#FFF8F0" opacity="0.6" d="M32 12c0-2.2 1.8-4 4-4s4 1.8 4 4v4h-8v-4zm12 0c0-2.2 1.8-4 4-4s4 1.8 4 4v4h-8v-4zm12 0c0-2.2 1.8-4 4-4s4 1.8 4 4v4h-8v-4z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 723 B |
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="96" height="96" viewBox="0 0 96 96" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="96" height="96" rx="14" fill="#FF5E1A"/>
|
||||
<g transform="translate(9, 9) scale(0.96)">
|
||||
<path fill="#FFF8F0" d="M60 20H20c-2.2 0-4 1.8-4 4v44c0 6.6 5.4 12 12 12h24c6.6 0 12-5.4 12-12V24c0-2.2-1.8-4-4-4zm-8 4v4H28v-4h24zm8 44c0 4.4-3.6 8-8 8H28c-4.4 0-8-3.6-8-8V32h40v36z"/>
|
||||
<path fill="#FFF8F0" d="M72 32h-4v8h4c2.2 0 4 1.8 4 4v8c0 2.2-1.8 4-4 4h-4v8h4c6.6 0 12-5.4 12-12v-8c0-6.6-5.4-12-12-12z"/>
|
||||
<path fill="#FFF8F0" opacity="0.6" d="M32 12c0-2.2 1.8-4 4-4s4 1.8 4 4v4h-8v-4zm12 0c0-2.2 1.8-4 4-4s4 1.8 4 4v4h-8v-4zm12 0c0-2.2 1.8-4 4-4s4 1.8 4 4v4h-8v-4z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 723 B |
|
After Width: | Height: | Size: 134 KiB |
|
After Width: | Height: | Size: 87 KiB |
@@ -0,0 +1,312 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=5" />
|
||||
<meta name="theme-color" content="#8B4513" />
|
||||
|
||||
<!-- Primary SEO Meta Tags -->
|
||||
<title>Tom's Java Jive | Premium Coffee Beans & Fresh Roasted Coffee | Weatherford TX</title>
|
||||
<meta name="title" content="Tom's Java Jive | Premium Coffee Beans & Fresh Roasted Coffee | Weatherford TX" />
|
||||
<meta name="description" content="Shop premium coffee beans and freshly roasted coffee grounds at Tom's Java Jive. Hand-selected, expertly roasted, delivered fresh to your door. Located in Weatherford, Texas. Free shipping on orders over $50!" />
|
||||
<meta name="keywords" content="coffee beans, fresh roasted coffee, premium coffee, coffee grounds, Weatherford Texas coffee, coffee shop, espresso beans, arabica coffee, single origin coffee, coffee subscription, gift cards, local coffee roaster" />
|
||||
<meta name="author" content="Tom's Java Jive" />
|
||||
<meta name="robots" content="index, follow, max-image-preview:large, max-snippet:-1, max-video-preview:-1" />
|
||||
<meta name="googlebot" content="index, follow" />
|
||||
<meta name="bingbot" content="index, follow" />
|
||||
<link rel="canonical" href="https://tomsjavaJive.com" />
|
||||
|
||||
<!-- Open Graph / Facebook -->
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:url" content="https://tomsjavaJive.com/" />
|
||||
<meta property="og:title" content="Tom's Java Jive | Premium Coffee Beans & Fresh Roasted Coffee" />
|
||||
<meta property="og:description" content="Shop premium coffee beans and freshly roasted coffee grounds. Hand-selected, expertly roasted, delivered fresh to your door. Free shipping on orders over $50!" />
|
||||
<meta property="og:image" content="https://tomsjavaJive.com/og-image.jpg" />
|
||||
<meta property="og:image:width" content="1200" />
|
||||
<meta property="og:image:height" content="630" />
|
||||
<meta property="og:locale" content="en_US" />
|
||||
<meta property="og:site_name" content="Tom's Java Jive" />
|
||||
|
||||
<!-- Twitter -->
|
||||
<meta property="twitter:card" content="summary_large_image" />
|
||||
<meta property="twitter:url" content="https://tomsjavaJive.com/" />
|
||||
<meta property="twitter:title" content="Tom's Java Jive | Premium Coffee Beans & Fresh Roasted Coffee" />
|
||||
<meta property="twitter:description" content="Shop premium coffee beans and freshly roasted coffee grounds. Hand-selected, expertly roasted, delivered fresh to your door." />
|
||||
<meta property="twitter:image" content="https://tomsjavaJive.com/og-image.jpg" />
|
||||
|
||||
<!-- Additional SEO Meta Tags -->
|
||||
<meta name="geo.region" content="US-TX" />
|
||||
<meta name="geo.placename" content="Weatherford" />
|
||||
<meta name="geo.position" content="32.7593;-97.7972" />
|
||||
<meta name="ICBM" content="32.7593, -97.7972" />
|
||||
<meta name="rating" content="General" />
|
||||
<meta name="revisit-after" content="7 days" />
|
||||
<meta name="distribution" content="global" />
|
||||
|
||||
<!-- PWA Meta Tags -->
|
||||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
|
||||
<meta name="apple-mobile-web-app-title" content="Java Jive" />
|
||||
<meta name="mobile-web-app-capable" content="yes" />
|
||||
<meta name="application-name" content="Tom's Java Jive" />
|
||||
<meta name="msapplication-TileColor" content="#8B4513" />
|
||||
<meta name="msapplication-config" content="/browserconfig.xml" />
|
||||
<link rel="apple-touch-icon" href="/icons/icon-192x192.png" />
|
||||
<link rel="apple-touch-icon" sizes="152x152" href="/icons/icon-152x152.png" />
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="/icons/icon-180x180.png" />
|
||||
<link rel="apple-touch-icon" sizes="167x167" href="/icons/icon-167x167.png" />
|
||||
<link rel="manifest" href="/manifest.json" />
|
||||
|
||||
<!-- Preconnect for Performance -->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
<link rel="dns-prefetch" href="https://fonts.googleapis.com" />
|
||||
<link rel="dns-prefetch" href="https://js.stripe.com" />
|
||||
|
||||
<!-- Fonts -->
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Playfair+Display:wght@400;500;600;700&display=swap" rel="stylesheet" />
|
||||
|
||||
<!-- Favicon -->
|
||||
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="/icons/icon-32x32.png" />
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="/icons/icon-16x16.png" />
|
||||
|
||||
<!-- Structured Data - Local Business -->
|
||||
<script type="application/ld+json">
|
||||
{
|
||||
"@context": "https://schema.org",
|
||||
"@type": "CoffeeStore",
|
||||
"name": "Tom's Java Jive",
|
||||
"description": "Premium coffee beans and freshly roasted coffee grounds. Hand-selected, expertly roasted, delivered fresh to your door.",
|
||||
"url": "https://tomsjavaJive.com",
|
||||
"telephone": "+1-817-266-2022",
|
||||
"email": "hello@tomsjavaJive.com",
|
||||
"address": {
|
||||
"@type": "PostalAddress",
|
||||
"streetAddress": "",
|
||||
"addressLocality": "Weatherford",
|
||||
"addressRegion": "TX",
|
||||
"postalCode": "",
|
||||
"addressCountry": "US"
|
||||
},
|
||||
"geo": {
|
||||
"@type": "GeoCoordinates",
|
||||
"latitude": 32.7593,
|
||||
"longitude": -97.7972
|
||||
},
|
||||
"openingHoursSpecification": [
|
||||
{
|
||||
"@type": "OpeningHoursSpecification",
|
||||
"dayOfWeek": ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"],
|
||||
"opens": "07:00",
|
||||
"closes": "18:00"
|
||||
},
|
||||
{
|
||||
"@type": "OpeningHoursSpecification",
|
||||
"dayOfWeek": ["Saturday"],
|
||||
"opens": "08:00",
|
||||
"closes": "16:00"
|
||||
}
|
||||
],
|
||||
"priceRange": "$$",
|
||||
"servesCuisine": "Coffee",
|
||||
"paymentAccepted": "Cash, Credit Card, Debit Card, Apple Pay, Google Pay",
|
||||
"currenciesAccepted": "USD",
|
||||
"hasMenu": "https://tomsjavaJive.com/shop",
|
||||
"sameAs": [
|
||||
"https://facebook.com/tomsjavaJive",
|
||||
"https://instagram.com/tomsjavaJive",
|
||||
"https://twitter.com/tomsjavaJive"
|
||||
],
|
||||
"image": "https://tomsjavaJive.com/og-image.jpg",
|
||||
"aggregateRating": {
|
||||
"@type": "AggregateRating",
|
||||
"ratingValue": "4.8",
|
||||
"reviewCount": "127"
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- Structured Data - Website -->
|
||||
<script type="application/ld+json">
|
||||
{
|
||||
"@context": "https://schema.org",
|
||||
"@type": "WebSite",
|
||||
"name": "Tom's Java Jive",
|
||||
"url": "https://tomsjavaJive.com",
|
||||
"potentialAction": {
|
||||
"@type": "SearchAction",
|
||||
"target": "https://tomsjavaJive.com/shop?search={search_term_string}",
|
||||
"query-input": "required name=search_term_string"
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- Structured Data - Organization -->
|
||||
<script type="application/ld+json">
|
||||
{
|
||||
"@context": "https://schema.org",
|
||||
"@type": "Organization",
|
||||
"name": "Tom's Java Jive",
|
||||
"url": "https://tomsjavaJive.com",
|
||||
"logo": "https://tomsjavaJive.com/icons/icon-512x512.png",
|
||||
"contactPoint": {
|
||||
"@type": "ContactPoint",
|
||||
"telephone": "+1-817-266-2022",
|
||||
"contactType": "customer service",
|
||||
"availableLanguage": ["English", "Spanish"]
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<script>window.addEventListener("error",function(e){if(e.error instanceof DOMException&&e.error.name==="DataCloneError"&&e.message&&e.message.includes("PerformanceServerTiming")){e.stopImmediatePropagation();e.preventDefault()}},true);</script>
|
||||
<script src="https://assets.emergent.sh/scripts/emergent-main.js"></script>
|
||||
|
||||
<!-- Register Service Worker -->
|
||||
<script>
|
||||
if ('serviceWorker' in navigator) {
|
||||
window.addEventListener('load', () => {
|
||||
navigator.serviceWorker.register('/service-worker.js')
|
||||
.then(reg => console.log('SW registered'))
|
||||
.catch(err => console.log('SW registration failed:', err));
|
||||
});
|
||||
}
|
||||
</script>
|
||||
<script defer src="/static/js/bundle.js"></script><script>
|
||||
if(window.self!==window.top){
|
||||
var s=document.createElement("script");s.src="/visual-edit-overlay.js";document.head.appendChild(s);
|
||||
window.tailwind=window.tailwind||{};tailwind.config={corePlugins:{preflight:false}};var t=document.createElement("script");t.src="https://cdn.tailwindcss.com";document.head.appendChild(t);
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<div id="root"></div>
|
||||
<!--
|
||||
This HTML file is a template.
|
||||
If you open it directly in the browser, you will see an empty page.
|
||||
|
||||
You can add webfonts, meta tags, or analytics to this file.
|
||||
The build step will place the bundled scripts into the <body> tag.
|
||||
|
||||
To begin the development, run `npm start` or `yarn start`.
|
||||
To create a production bundle, use `npm run build` or `yarn build`.
|
||||
-->
|
||||
<a
|
||||
id="emergent-badge"
|
||||
target="_blank"
|
||||
href="https://app.emergent.sh/?utm_source=emergent-badge"
|
||||
style="
|
||||
display: inline-flex !important;
|
||||
box-sizing: border-box;
|
||||
width: 178px;
|
||||
height: 40px;
|
||||
padding: 8px 12px 8px 12px;
|
||||
align-items: center !important;
|
||||
gap: 8px;
|
||||
border-radius: 50px !important;
|
||||
background: #000 !important;
|
||||
position: fixed !important;
|
||||
bottom: 16px;
|
||||
right: 16px;
|
||||
text-decoration: none;
|
||||
font-family: -apple-system, BlinkMacSystemFont,
|
||||
"Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell,
|
||||
"Open Sans", "Helvetica Neue",
|
||||
sans-serif !important;
|
||||
font-size: 12px !important;
|
||||
z-index: 9999 !important;
|
||||
"
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16" fill="none">
|
||||
<path d="M15.5702 8.13142C15.7729 8.0412 16.0007 8.18878 15.9892 8.4103C15.8374 11.3192 14.0965 14.0405 11.2531 15.3065C8.40964 16.5725 5.2224 16.0453 2.95912 14.2117C2.78676 14.072 2.82955 13.804 3.03219 13.7137L4.95677 12.8568C5.04866 12.8159 5.15446 12.823 5.24204 12.8725C6.73377 13.7153 8.59176 13.8649 10.2772 13.1145C11.9626 12.3641 13.0947 10.8833 13.4665 9.21075C13.4883 9.11256 13.5539 9.02918 13.6457 8.98827L15.5702 8.13142Z" fill="white"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M15.3066 4.74698L15.5067 5.19653C15.5759 5.35178 15.5061 5.53366 15.3508 5.60278L1.29992 11.8586C1.14467 11.9278 0.962794 11.8579 0.893675 11.7027L0.701732 11.2716L0.693457 11.2531C-1.10317 7.21778 0.711626 2.49007 4.74692 0.693443C8.78221 -1.10318 13.51 0.711693 15.3066 4.74698ZM2.82356 8.55367C2.63552 8.63739 2.41991 8.51617 2.40853 8.31065C2.28373 6.05724 3.53858 3.85787 5.72286 2.88536C7.90715 1.91286 10.3813 2.45199 11.9724 4.05256C12.1175 4.19854 12.0633 4.43988 11.8753 4.5236L2.82356 8.55367Z" fill="white"/>
|
||||
</svg>
|
||||
<p
|
||||
style="
|
||||
color: #FFF !important;
|
||||
font-family: 'Inter', sans-serif !important;
|
||||
font-size: 13px !important;
|
||||
font-style: normal !important;
|
||||
font-weight: 600 !important;
|
||||
line-height: 20px !important;
|
||||
margin: 0 !important;
|
||||
white-space: nowrap !important;
|
||||
"
|
||||
>
|
||||
Made with Emergent
|
||||
</p>
|
||||
</a>
|
||||
<script>
|
||||
!(function (t, e) {
|
||||
var o, n, p, r;
|
||||
e.__SV ||
|
||||
((window.posthog = e),
|
||||
(e._i = []),
|
||||
(e.init = function (i, s, a) {
|
||||
function g(t, e) {
|
||||
var o = e.split(".");
|
||||
2 == o.length && ((t = t[o[0]]), (e = o[1])),
|
||||
(t[e] = function () {
|
||||
t.push(
|
||||
[e].concat(
|
||||
Array.prototype.slice.call(
|
||||
arguments,
|
||||
0,
|
||||
),
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
((p = t.createElement("script")).type =
|
||||
"text/javascript"),
|
||||
(p.crossOrigin = "anonymous"),
|
||||
(p.async = !0),
|
||||
(p.src =
|
||||
s.api_host.replace(
|
||||
".i.posthog.com",
|
||||
"-assets.i.posthog.com",
|
||||
) + "/static/array.js"),
|
||||
(r =
|
||||
t.getElementsByTagName(
|
||||
"script",
|
||||
)[0]).parentNode.insertBefore(p, r);
|
||||
var u = e;
|
||||
for (
|
||||
void 0 !== a ? (u = e[a] = []) : (a = "posthog"),
|
||||
u.people = u.people || [],
|
||||
u.toString = function (t) {
|
||||
var e = "posthog";
|
||||
return (
|
||||
"posthog" !== a && (e += "." + a),
|
||||
t || (e += " (stub)"),
|
||||
e
|
||||
);
|
||||
},
|
||||
u.people.toString = function () {
|
||||
return u.toString(1) + ".people (stub)";
|
||||
},
|
||||
o =
|
||||
"init me ws ys ps bs capture je Di ks register register_once register_for_session unregister unregister_for_session Ps getFeatureFlag getFeatureFlagPayload isFeatureEnabled reloadFeatureFlags updateEarlyAccessFeatureEnrollment getEarlyAccessFeatures on onFeatureFlags onSurveysLoaded onSessionId getSurveys getActiveMatchingSurveys renderSurvey canRenderSurvey canRenderSurveyAsync identify setPersonProperties group resetGroups setPersonPropertiesForFlags resetPersonPropertiesForFlags setGroupPropertiesForFlags resetGroupPropertiesForFlags reset get_distinct_id getGroups get_session_id get_session_replay_url alias set_config startSessionRecording stopSessionRecording sessionRecordingStarted captureException loadToolbar get_property getSessionProperty Es $s createPersonProfile Is opt_in_capturing opt_out_capturing has_opted_in_capturing has_opted_out_capturing clear_opt_in_out_capturing Ss debug xs getPageViewId captureTraceFeedback captureTraceMetric".split(
|
||||
" ",
|
||||
),
|
||||
n = 0;
|
||||
n < o.length;
|
||||
n++
|
||||
)
|
||||
g(u, o[n]);
|
||||
e._i.push([i, s, a]);
|
||||
}),
|
||||
(e.__SV = 1));
|
||||
})(document, window.posthog || []);
|
||||
posthog.init("phc_xAvL2Iq4tFmANRE7kzbKwaSqp1HJjN7x48s3vr0CMjs", {
|
||||
api_host: "https://us.i.posthog.com",
|
||||
person_profiles: "identified_only", // or 'always' to create profiles for anonymous users as well,
|
||||
session_recording: {
|
||||
recordCrossOriginIframes: true,
|
||||
capturePerformance: false,
|
||||
},
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
After Width: | Height: | Size: 82 KiB |
|
After Width: | Height: | Size: 115 KiB |
@@ -0,0 +1,312 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=5" />
|
||||
<meta name="theme-color" content="#8B4513" />
|
||||
|
||||
<!-- Primary SEO Meta Tags -->
|
||||
<title>Tom's Java Jive | Premium Coffee Beans & Fresh Roasted Coffee | Weatherford TX</title>
|
||||
<meta name="title" content="Tom's Java Jive | Premium Coffee Beans & Fresh Roasted Coffee | Weatherford TX" />
|
||||
<meta name="description" content="Shop premium coffee beans and freshly roasted coffee grounds at Tom's Java Jive. Hand-selected, expertly roasted, delivered fresh to your door. Located in Weatherford, Texas. Free shipping on orders over $50!" />
|
||||
<meta name="keywords" content="coffee beans, fresh roasted coffee, premium coffee, coffee grounds, Weatherford Texas coffee, coffee shop, espresso beans, arabica coffee, single origin coffee, coffee subscription, gift cards, local coffee roaster" />
|
||||
<meta name="author" content="Tom's Java Jive" />
|
||||
<meta name="robots" content="index, follow, max-image-preview:large, max-snippet:-1, max-video-preview:-1" />
|
||||
<meta name="googlebot" content="index, follow" />
|
||||
<meta name="bingbot" content="index, follow" />
|
||||
<link rel="canonical" href="https://tomsjavaJive.com" />
|
||||
|
||||
<!-- Open Graph / Facebook -->
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:url" content="https://tomsjavaJive.com/" />
|
||||
<meta property="og:title" content="Tom's Java Jive | Premium Coffee Beans & Fresh Roasted Coffee" />
|
||||
<meta property="og:description" content="Shop premium coffee beans and freshly roasted coffee grounds. Hand-selected, expertly roasted, delivered fresh to your door. Free shipping on orders over $50!" />
|
||||
<meta property="og:image" content="https://tomsjavaJive.com/og-image.jpg" />
|
||||
<meta property="og:image:width" content="1200" />
|
||||
<meta property="og:image:height" content="630" />
|
||||
<meta property="og:locale" content="en_US" />
|
||||
<meta property="og:site_name" content="Tom's Java Jive" />
|
||||
|
||||
<!-- Twitter -->
|
||||
<meta property="twitter:card" content="summary_large_image" />
|
||||
<meta property="twitter:url" content="https://tomsjavaJive.com/" />
|
||||
<meta property="twitter:title" content="Tom's Java Jive | Premium Coffee Beans & Fresh Roasted Coffee" />
|
||||
<meta property="twitter:description" content="Shop premium coffee beans and freshly roasted coffee grounds. Hand-selected, expertly roasted, delivered fresh to your door." />
|
||||
<meta property="twitter:image" content="https://tomsjavaJive.com/og-image.jpg" />
|
||||
|
||||
<!-- Additional SEO Meta Tags -->
|
||||
<meta name="geo.region" content="US-TX" />
|
||||
<meta name="geo.placename" content="Weatherford" />
|
||||
<meta name="geo.position" content="32.7593;-97.7972" />
|
||||
<meta name="ICBM" content="32.7593, -97.7972" />
|
||||
<meta name="rating" content="General" />
|
||||
<meta name="revisit-after" content="7 days" />
|
||||
<meta name="distribution" content="global" />
|
||||
|
||||
<!-- PWA Meta Tags -->
|
||||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
|
||||
<meta name="apple-mobile-web-app-title" content="Java Jive" />
|
||||
<meta name="mobile-web-app-capable" content="yes" />
|
||||
<meta name="application-name" content="Tom's Java Jive" />
|
||||
<meta name="msapplication-TileColor" content="#8B4513" />
|
||||
<meta name="msapplication-config" content="/browserconfig.xml" />
|
||||
<link rel="apple-touch-icon" href="/icons/icon-192x192.png" />
|
||||
<link rel="apple-touch-icon" sizes="152x152" href="/icons/icon-152x152.png" />
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="/icons/icon-180x180.png" />
|
||||
<link rel="apple-touch-icon" sizes="167x167" href="/icons/icon-167x167.png" />
|
||||
<link rel="manifest" href="/manifest.json" />
|
||||
|
||||
<!-- Preconnect for Performance -->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
<link rel="dns-prefetch" href="https://fonts.googleapis.com" />
|
||||
<link rel="dns-prefetch" href="https://js.stripe.com" />
|
||||
|
||||
<!-- Fonts -->
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Playfair+Display:wght@400;500;600;700&display=swap" rel="stylesheet" />
|
||||
|
||||
<!-- Favicon -->
|
||||
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="/icons/icon-32x32.png" />
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="/icons/icon-16x16.png" />
|
||||
|
||||
<!-- Structured Data - Local Business -->
|
||||
<script type="application/ld+json">
|
||||
{
|
||||
"@context": "https://schema.org",
|
||||
"@type": "CoffeeStore",
|
||||
"name": "Tom's Java Jive",
|
||||
"description": "Premium coffee beans and freshly roasted coffee grounds. Hand-selected, expertly roasted, delivered fresh to your door.",
|
||||
"url": "https://tomsjavaJive.com",
|
||||
"telephone": "+1-817-266-2022",
|
||||
"email": "hello@tomsjavaJive.com",
|
||||
"address": {
|
||||
"@type": "PostalAddress",
|
||||
"streetAddress": "",
|
||||
"addressLocality": "Weatherford",
|
||||
"addressRegion": "TX",
|
||||
"postalCode": "",
|
||||
"addressCountry": "US"
|
||||
},
|
||||
"geo": {
|
||||
"@type": "GeoCoordinates",
|
||||
"latitude": 32.7593,
|
||||
"longitude": -97.7972
|
||||
},
|
||||
"openingHoursSpecification": [
|
||||
{
|
||||
"@type": "OpeningHoursSpecification",
|
||||
"dayOfWeek": ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"],
|
||||
"opens": "07:00",
|
||||
"closes": "18:00"
|
||||
},
|
||||
{
|
||||
"@type": "OpeningHoursSpecification",
|
||||
"dayOfWeek": ["Saturday"],
|
||||
"opens": "08:00",
|
||||
"closes": "16:00"
|
||||
}
|
||||
],
|
||||
"priceRange": "$$",
|
||||
"servesCuisine": "Coffee",
|
||||
"paymentAccepted": "Cash, Credit Card, Debit Card, Apple Pay, Google Pay",
|
||||
"currenciesAccepted": "USD",
|
||||
"hasMenu": "https://tomsjavaJive.com/shop",
|
||||
"sameAs": [
|
||||
"https://facebook.com/tomsjavaJive",
|
||||
"https://instagram.com/tomsjavaJive",
|
||||
"https://twitter.com/tomsjavaJive"
|
||||
],
|
||||
"image": "https://tomsjavaJive.com/og-image.jpg",
|
||||
"aggregateRating": {
|
||||
"@type": "AggregateRating",
|
||||
"ratingValue": "4.8",
|
||||
"reviewCount": "127"
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- Structured Data - Website -->
|
||||
<script type="application/ld+json">
|
||||
{
|
||||
"@context": "https://schema.org",
|
||||
"@type": "WebSite",
|
||||
"name": "Tom's Java Jive",
|
||||
"url": "https://tomsjavaJive.com",
|
||||
"potentialAction": {
|
||||
"@type": "SearchAction",
|
||||
"target": "https://tomsjavaJive.com/shop?search={search_term_string}",
|
||||
"query-input": "required name=search_term_string"
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- Structured Data - Organization -->
|
||||
<script type="application/ld+json">
|
||||
{
|
||||
"@context": "https://schema.org",
|
||||
"@type": "Organization",
|
||||
"name": "Tom's Java Jive",
|
||||
"url": "https://tomsjavaJive.com",
|
||||
"logo": "https://tomsjavaJive.com/icons/icon-512x512.png",
|
||||
"contactPoint": {
|
||||
"@type": "ContactPoint",
|
||||
"telephone": "+1-817-266-2022",
|
||||
"contactType": "customer service",
|
||||
"availableLanguage": ["English", "Spanish"]
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<script>window.addEventListener("error",function(e){if(e.error instanceof DOMException&&e.error.name==="DataCloneError"&&e.message&&e.message.includes("PerformanceServerTiming")){e.stopImmediatePropagation();e.preventDefault()}},true);</script>
|
||||
<script src="https://assets.emergent.sh/scripts/emergent-main.js"></script>
|
||||
|
||||
<!-- Register Service Worker -->
|
||||
<script>
|
||||
if ('serviceWorker' in navigator) {
|
||||
window.addEventListener('load', () => {
|
||||
navigator.serviceWorker.register('/service-worker.js')
|
||||
.then(reg => console.log('SW registered'))
|
||||
.catch(err => console.log('SW registration failed:', err));
|
||||
});
|
||||
}
|
||||
</script>
|
||||
<script defer src="/static/js/bundle.js"></script><script>
|
||||
if(window.self!==window.top){
|
||||
var s=document.createElement("script");s.src="/visual-edit-overlay.js";document.head.appendChild(s);
|
||||
window.tailwind=window.tailwind||{};tailwind.config={corePlugins:{preflight:false}};var t=document.createElement("script");t.src="https://cdn.tailwindcss.com";document.head.appendChild(t);
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<div id="root"></div>
|
||||
<!--
|
||||
This HTML file is a template.
|
||||
If you open it directly in the browser, you will see an empty page.
|
||||
|
||||
You can add webfonts, meta tags, or analytics to this file.
|
||||
The build step will place the bundled scripts into the <body> tag.
|
||||
|
||||
To begin the development, run `npm start` or `yarn start`.
|
||||
To create a production bundle, use `npm run build` or `yarn build`.
|
||||
-->
|
||||
<a
|
||||
id="emergent-badge"
|
||||
target="_blank"
|
||||
href="https://app.emergent.sh/?utm_source=emergent-badge"
|
||||
style="
|
||||
display: inline-flex !important;
|
||||
box-sizing: border-box;
|
||||
width: 178px;
|
||||
height: 40px;
|
||||
padding: 8px 12px 8px 12px;
|
||||
align-items: center !important;
|
||||
gap: 8px;
|
||||
border-radius: 50px !important;
|
||||
background: #000 !important;
|
||||
position: fixed !important;
|
||||
bottom: 16px;
|
||||
right: 16px;
|
||||
text-decoration: none;
|
||||
font-family: -apple-system, BlinkMacSystemFont,
|
||||
"Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell,
|
||||
"Open Sans", "Helvetica Neue",
|
||||
sans-serif !important;
|
||||
font-size: 12px !important;
|
||||
z-index: 9999 !important;
|
||||
"
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16" fill="none">
|
||||
<path d="M15.5702 8.13142C15.7729 8.0412 16.0007 8.18878 15.9892 8.4103C15.8374 11.3192 14.0965 14.0405 11.2531 15.3065C8.40964 16.5725 5.2224 16.0453 2.95912 14.2117C2.78676 14.072 2.82955 13.804 3.03219 13.7137L4.95677 12.8568C5.04866 12.8159 5.15446 12.823 5.24204 12.8725C6.73377 13.7153 8.59176 13.8649 10.2772 13.1145C11.9626 12.3641 13.0947 10.8833 13.4665 9.21075C13.4883 9.11256 13.5539 9.02918 13.6457 8.98827L15.5702 8.13142Z" fill="white"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M15.3066 4.74698L15.5067 5.19653C15.5759 5.35178 15.5061 5.53366 15.3508 5.60278L1.29992 11.8586C1.14467 11.9278 0.962794 11.8579 0.893675 11.7027L0.701732 11.2716L0.693457 11.2531C-1.10317 7.21778 0.711626 2.49007 4.74692 0.693443C8.78221 -1.10318 13.51 0.711693 15.3066 4.74698ZM2.82356 8.55367C2.63552 8.63739 2.41991 8.51617 2.40853 8.31065C2.28373 6.05724 3.53858 3.85787 5.72286 2.88536C7.90715 1.91286 10.3813 2.45199 11.9724 4.05256C12.1175 4.19854 12.0633 4.43988 11.8753 4.5236L2.82356 8.55367Z" fill="white"/>
|
||||
</svg>
|
||||
<p
|
||||
style="
|
||||
color: #FFF !important;
|
||||
font-family: 'Inter', sans-serif !important;
|
||||
font-size: 13px !important;
|
||||
font-style: normal !important;
|
||||
font-weight: 600 !important;
|
||||
line-height: 20px !important;
|
||||
margin: 0 !important;
|
||||
white-space: nowrap !important;
|
||||
"
|
||||
>
|
||||
Made with Emergent
|
||||
</p>
|
||||
</a>
|
||||
<script>
|
||||
!(function (t, e) {
|
||||
var o, n, p, r;
|
||||
e.__SV ||
|
||||
((window.posthog = e),
|
||||
(e._i = []),
|
||||
(e.init = function (i, s, a) {
|
||||
function g(t, e) {
|
||||
var o = e.split(".");
|
||||
2 == o.length && ((t = t[o[0]]), (e = o[1])),
|
||||
(t[e] = function () {
|
||||
t.push(
|
||||
[e].concat(
|
||||
Array.prototype.slice.call(
|
||||
arguments,
|
||||
0,
|
||||
),
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
((p = t.createElement("script")).type =
|
||||
"text/javascript"),
|
||||
(p.crossOrigin = "anonymous"),
|
||||
(p.async = !0),
|
||||
(p.src =
|
||||
s.api_host.replace(
|
||||
".i.posthog.com",
|
||||
"-assets.i.posthog.com",
|
||||
) + "/static/array.js"),
|
||||
(r =
|
||||
t.getElementsByTagName(
|
||||
"script",
|
||||
)[0]).parentNode.insertBefore(p, r);
|
||||
var u = e;
|
||||
for (
|
||||
void 0 !== a ? (u = e[a] = []) : (a = "posthog"),
|
||||
u.people = u.people || [],
|
||||
u.toString = function (t) {
|
||||
var e = "posthog";
|
||||
return (
|
||||
"posthog" !== a && (e += "." + a),
|
||||
t || (e += " (stub)"),
|
||||
e
|
||||
);
|
||||
},
|
||||
u.people.toString = function () {
|
||||
return u.toString(1) + ".people (stub)";
|
||||
},
|
||||
o =
|
||||
"init me ws ys ps bs capture je Di ks register register_once register_for_session unregister unregister_for_session Ps getFeatureFlag getFeatureFlagPayload isFeatureEnabled reloadFeatureFlags updateEarlyAccessFeatureEnrollment getEarlyAccessFeatures on onFeatureFlags onSurveysLoaded onSessionId getSurveys getActiveMatchingSurveys renderSurvey canRenderSurvey canRenderSurveyAsync identify setPersonProperties group resetGroups setPersonPropertiesForFlags resetPersonPropertiesForFlags setGroupPropertiesForFlags resetGroupPropertiesForFlags reset get_distinct_id getGroups get_session_id get_session_replay_url alias set_config startSessionRecording stopSessionRecording sessionRecordingStarted captureException loadToolbar get_property getSessionProperty Es $s createPersonProfile Is opt_in_capturing opt_out_capturing has_opted_in_capturing has_opted_out_capturing clear_opt_in_out_capturing Ss debug xs getPageViewId captureTraceFeedback captureTraceMetric".split(
|
||||
" ",
|
||||
),
|
||||
n = 0;
|
||||
n < o.length;
|
||||
n++
|
||||
)
|
||||
g(u, o[n]);
|
||||
e._i.push([i, s, a]);
|
||||
}),
|
||||
(e.__SV = 1));
|
||||
})(document, window.posthog || []);
|
||||
posthog.init("phc_xAvL2Iq4tFmANRE7kzbKwaSqp1HJjN7x48s3vr0CMjs", {
|
||||
api_host: "https://us.i.posthog.com",
|
||||
person_profiles: "identified_only", // or 'always' to create profiles for anonymous users as well,
|
||||
session_recording: {
|
||||
recordCrossOriginIframes: true,
|
||||
capturePerformance: false,
|
||||
},
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,13 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
|
||||
<rect width="512" height="512" fill="#FDFBF7"/>
|
||||
<g fill="#FF5E1A">
|
||||
<!-- Coffee cup -->
|
||||
<path d="M128 160h192c8.8 0 16 7.2 16 16v160c0 44.2-35.8 80-80 80H192c-44.2 0-80-35.8-80-80V176c0-8.8 7.2-16 16-16z"/>
|
||||
<!-- Cup handle -->
|
||||
<path d="M336 192h32c26.5 0 48 21.5 48 48v32c0 26.5-21.5 48-48 48h-32v-128z"/>
|
||||
<!-- Steam lines -->
|
||||
<path d="M160 128c0-17.7 14.3-32 32-32s32 14.3 32 32c0-17.7 14.3-32 32-32s32 14.3 32 32" stroke="#FF5E1A" stroke-width="16" fill="none" stroke-linecap="round"/>
|
||||
</g>
|
||||
<!-- Plate -->
|
||||
<ellipse cx="224" cy="420" rx="120" ry="20" fill="#E8E2D9"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 671 B |
|
After Width: | Height: | Size: 134 KiB |
@@ -0,0 +1,24 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 300 80">
|
||||
<defs>
|
||||
<linearGradient id="coffeeGrad" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#8B4513"/>
|
||||
<stop offset="100%" style="stop-color:#E86A33"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
|
||||
<!-- Coffee Cup Icon -->
|
||||
<g transform="translate(10, 10)">
|
||||
<!-- Cup body -->
|
||||
<path d="M5 15 L10 55 Q12 60 20 60 L40 60 Q48 60 50 55 L55 15 Z" fill="url(#coffeeGrad)"/>
|
||||
<!-- Cup handle -->
|
||||
<path d="M55 20 Q70 20 70 35 Q70 50 55 50" stroke="#8B4513" stroke-width="4" fill="none"/>
|
||||
<!-- Steam -->
|
||||
<path d="M20 5 Q25 0 20 -5 Q15 -10 20 -15" stroke="#E86A33" stroke-width="2" fill="none" opacity="0.7"/>
|
||||
<path d="M30 8 Q35 3 30 -2 Q25 -7 30 -12" stroke="#E86A33" stroke-width="2" fill="none" opacity="0.5"/>
|
||||
<path d="M40 5 Q45 0 40 -5 Q35 -10 40 -15" stroke="#E86A33" stroke-width="2" fill="none" opacity="0.7"/>
|
||||
</g>
|
||||
|
||||
<!-- Text -->
|
||||
<text x="85" y="35" font-family="Georgia, serif" font-size="28" font-weight="bold" fill="#8B4513">Tom's</text>
|
||||
<text x="85" y="60" font-family="Georgia, serif" font-size="24" font-weight="bold" fill="#E86A33">Java Jive</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
@@ -0,0 +1,7 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 400 400">
|
||||
<rect width="400" height="400" fill="#F5F0EB"/>
|
||||
<circle cx="200" cy="180" r="100" fill="#D4A574"/>
|
||||
<ellipse cx="200" cy="180" rx="70" ry="30" fill="#8B4513"/>
|
||||
<path d="M130 180 Q130 280 200 280 Q270 280 270 180" fill="#D4A574"/>
|
||||
<text x="200" y="340" text-anchor="middle" font-family="Georgia, serif" font-size="24" fill="#8B4513">Product Image</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 431 B |
|
After Width: | Height: | Size: 64 KiB |
@@ -0,0 +1,394 @@
|
||||
/**
|
||||
* Tom's Java Jive - Main JavaScript
|
||||
*/
|
||||
|
||||
// ================================
|
||||
// Toast Notifications
|
||||
// ================================
|
||||
const ToastManager = {
|
||||
container: null,
|
||||
|
||||
init() {
|
||||
if (!this.container) {
|
||||
this.container = document.createElement('div');
|
||||
this.container.className = 'toast-container';
|
||||
document.body.appendChild(this.container);
|
||||
}
|
||||
},
|
||||
|
||||
show(message, type = 'success', duration = 3000) {
|
||||
this.init();
|
||||
|
||||
const toast = document.createElement('div');
|
||||
toast.className = `toast ${type}`;
|
||||
toast.innerHTML = `
|
||||
<i class="fas fa-${type === 'success' ? 'check-circle' : type === 'error' ? 'exclamation-circle' : 'info-circle'}"></i>
|
||||
<span>${message}</span>
|
||||
`;
|
||||
|
||||
this.container.appendChild(toast);
|
||||
|
||||
setTimeout(() => {
|
||||
toast.style.animation = 'slideIn 0.3s ease reverse';
|
||||
setTimeout(() => toast.remove(), 300);
|
||||
}, duration);
|
||||
},
|
||||
|
||||
success(message) { this.show(message, 'success'); },
|
||||
error(message) { this.show(message, 'error'); },
|
||||
info(message) { this.show(message, 'info'); }
|
||||
};
|
||||
|
||||
// ================================
|
||||
// Cart Functions
|
||||
// ================================
|
||||
async function addToCart(productId, quantity = 1) {
|
||||
try {
|
||||
const response = await fetch('/api/cart.php', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
action: 'add',
|
||||
product_id: productId,
|
||||
quantity: parseInt(quantity)
|
||||
})
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (data.error) {
|
||||
ToastManager.error(data.error);
|
||||
return;
|
||||
}
|
||||
|
||||
// Update cart count in header
|
||||
updateCartCount(data.cart_count);
|
||||
ToastManager.success(data.message || 'Added to cart!');
|
||||
|
||||
} catch (error) {
|
||||
console.error('Add to cart error:', error);
|
||||
ToastManager.error('Failed to add item to cart');
|
||||
}
|
||||
}
|
||||
|
||||
async function updateCartItem(productId, quantity) {
|
||||
try {
|
||||
const response = await fetch('/api/cart.php', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
action: 'update',
|
||||
product_id: productId,
|
||||
quantity: parseInt(quantity)
|
||||
})
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (data.error) {
|
||||
ToastManager.error(data.error);
|
||||
return;
|
||||
}
|
||||
|
||||
// Update cart count
|
||||
updateCartCount(data.cart_count);
|
||||
|
||||
// Reload page if cart is now empty or update display
|
||||
if (data.cart_count === 0) {
|
||||
location.reload();
|
||||
} else if (typeof updateCartDisplay === 'function') {
|
||||
updateCartDisplay(data);
|
||||
} else {
|
||||
location.reload();
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('Update cart error:', error);
|
||||
ToastManager.error('Failed to update cart');
|
||||
}
|
||||
}
|
||||
|
||||
async function removeFromCart(productId) {
|
||||
try {
|
||||
const response = await fetch('/api/cart.php', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
action: 'remove',
|
||||
product_id: productId
|
||||
})
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (data.error) {
|
||||
ToastManager.error(data.error);
|
||||
return;
|
||||
}
|
||||
|
||||
updateCartCount(data.cart_count);
|
||||
|
||||
// Remove item from DOM or reload
|
||||
const cartItem = document.querySelector(`.cart-item[data-product-id="${productId}"]`);
|
||||
if (cartItem) {
|
||||
cartItem.style.animation = 'slideIn 0.3s ease reverse';
|
||||
setTimeout(() => {
|
||||
cartItem.remove();
|
||||
if (data.cart_count === 0) {
|
||||
location.reload();
|
||||
}
|
||||
}, 300);
|
||||
} else {
|
||||
location.reload();
|
||||
}
|
||||
|
||||
ToastManager.success('Item removed from cart');
|
||||
|
||||
} catch (error) {
|
||||
console.error('Remove from cart error:', error);
|
||||
ToastManager.error('Failed to remove item');
|
||||
}
|
||||
}
|
||||
|
||||
function updateCartCount(count) {
|
||||
const cartCountEl = document.querySelector('.cart-count');
|
||||
if (cartCountEl) {
|
||||
if (count > 0) {
|
||||
cartCountEl.textContent = count;
|
||||
cartCountEl.style.display = 'flex';
|
||||
} else {
|
||||
cartCountEl.style.display = 'none';
|
||||
}
|
||||
} else if (count > 0) {
|
||||
const cartLink = document.querySelector('.cart-link');
|
||||
if (cartLink) {
|
||||
const badge = document.createElement('span');
|
||||
badge.className = 'cart-count';
|
||||
badge.textContent = count;
|
||||
cartLink.appendChild(badge);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ================================
|
||||
// Quantity Selectors
|
||||
// ================================
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// Quantity +/- buttons
|
||||
document.querySelectorAll('.qty-minus').forEach(btn => {
|
||||
btn.addEventListener('click', function() {
|
||||
const input = this.closest('.quantity-selector').querySelector('.qty-input');
|
||||
const current = parseInt(input.value) || 1;
|
||||
if (current > 1) {
|
||||
input.value = current - 1;
|
||||
input.dispatchEvent(new Event('change'));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
document.querySelectorAll('.qty-plus').forEach(btn => {
|
||||
btn.addEventListener('click', function() {
|
||||
const input = this.closest('.quantity-selector').querySelector('.qty-input');
|
||||
const current = parseInt(input.value) || 1;
|
||||
const max = parseInt(input.max) || 999;
|
||||
if (current < max) {
|
||||
input.value = current + 1;
|
||||
input.dispatchEvent(new Event('change'));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Add to cart buttons
|
||||
document.querySelectorAll('.add-to-cart-btn').forEach(btn => {
|
||||
btn.addEventListener('click', function(e) {
|
||||
if (!this.dataset.productId) return;
|
||||
|
||||
e.preventDefault();
|
||||
const productId = this.dataset.productId;
|
||||
const qtyInput = document.querySelector('.qty-input');
|
||||
const quantity = qtyInput ? parseInt(qtyInput.value) : 1;
|
||||
|
||||
addToCart(productId, quantity);
|
||||
});
|
||||
});
|
||||
|
||||
// Mobile menu toggle
|
||||
const menuToggle = document.querySelector('.mobile-menu-toggle');
|
||||
const navMenu = document.querySelector('.nav-menu');
|
||||
if (menuToggle && navMenu) {
|
||||
menuToggle.addEventListener('click', function() {
|
||||
navMenu.classList.toggle('active');
|
||||
this.querySelector('i').classList.toggle('fa-bars');
|
||||
this.querySelector('i').classList.toggle('fa-times');
|
||||
});
|
||||
}
|
||||
|
||||
// Confirm delete dialogs
|
||||
document.querySelectorAll('[data-confirm]').forEach(el => {
|
||||
el.addEventListener('click', function(e) {
|
||||
if (!confirm(this.dataset.confirm)) {
|
||||
e.preventDefault();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Auto-hide alerts
|
||||
document.querySelectorAll('.alert').forEach(alert => {
|
||||
setTimeout(() => {
|
||||
alert.style.opacity = '0';
|
||||
alert.style.transform = 'translateY(-10px)';
|
||||
setTimeout(() => alert.remove(), 300);
|
||||
}, 5000);
|
||||
});
|
||||
});
|
||||
|
||||
// ================================
|
||||
// Form Helpers
|
||||
// ================================
|
||||
function serializeForm(form) {
|
||||
const formData = new FormData(form);
|
||||
const data = {};
|
||||
formData.forEach((value, key) => {
|
||||
data[key] = value;
|
||||
});
|
||||
return data;
|
||||
}
|
||||
|
||||
async function submitForm(form, url, method = 'POST') {
|
||||
const data = serializeForm(form);
|
||||
|
||||
try {
|
||||
const response = await fetch(url, {
|
||||
method,
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(data)
|
||||
});
|
||||
|
||||
return await response.json();
|
||||
} catch (error) {
|
||||
console.error('Form submit error:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// ================================
|
||||
// Format Helpers
|
||||
// ================================
|
||||
function formatCurrency(amount) {
|
||||
return '$' + parseFloat(amount).toFixed(2);
|
||||
}
|
||||
|
||||
function formatDate(date) {
|
||||
return new Date(date).toLocaleDateString('en-US', {
|
||||
year: 'numeric',
|
||||
month: 'short',
|
||||
day: 'numeric'
|
||||
});
|
||||
}
|
||||
|
||||
// ================================
|
||||
// Loading States
|
||||
// ================================
|
||||
function setLoading(button, isLoading) {
|
||||
if (isLoading) {
|
||||
button.dataset.originalText = button.innerHTML;
|
||||
button.innerHTML = '<span class="loading"></span> Loading...';
|
||||
button.disabled = true;
|
||||
} else {
|
||||
button.innerHTML = button.dataset.originalText || button.innerHTML;
|
||||
button.disabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
// ================================
|
||||
// Image Preview
|
||||
// ================================
|
||||
function previewImage(input, previewId) {
|
||||
const preview = document.getElementById(previewId);
|
||||
if (!preview) return;
|
||||
|
||||
if (input.files && input.files[0]) {
|
||||
const reader = new FileReader();
|
||||
reader.onload = function(e) {
|
||||
preview.src = e.target.result;
|
||||
preview.style.display = 'block';
|
||||
};
|
||||
reader.readAsDataURL(input.files[0]);
|
||||
}
|
||||
}
|
||||
|
||||
// ================================
|
||||
// Newsletter Subscription
|
||||
// ================================
|
||||
document.querySelectorAll('.newsletter-form').forEach(form => {
|
||||
form.addEventListener('submit', async function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
const email = this.querySelector('input[type="email"]').value;
|
||||
const button = this.querySelector('button');
|
||||
|
||||
setLoading(button, true);
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/subscribe.php', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ email })
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (data.error) {
|
||||
ToastManager.error(data.error);
|
||||
} else {
|
||||
ToastManager.success('Thank you for subscribing!');
|
||||
this.reset();
|
||||
}
|
||||
} catch (error) {
|
||||
ToastManager.error('Subscription failed. Please try again.');
|
||||
} finally {
|
||||
setLoading(button, false);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// ================================
|
||||
// Debounce Helper
|
||||
// ================================
|
||||
function debounce(func, wait) {
|
||||
let timeout;
|
||||
return function executedFunction(...args) {
|
||||
const later = () => {
|
||||
clearTimeout(timeout);
|
||||
func(...args);
|
||||
};
|
||||
clearTimeout(timeout);
|
||||
timeout = setTimeout(later, wait);
|
||||
};
|
||||
}
|
||||
|
||||
// ================================
|
||||
// Local Storage Helpers
|
||||
// ================================
|
||||
const Storage = {
|
||||
get(key, defaultValue = null) {
|
||||
try {
|
||||
const item = localStorage.getItem(key);
|
||||
return item ? JSON.parse(item) : defaultValue;
|
||||
} catch {
|
||||
return defaultValue;
|
||||
}
|
||||
},
|
||||
|
||||
set(key, value) {
|
||||
try {
|
||||
localStorage.setItem(key, JSON.stringify(value));
|
||||
} catch (e) {
|
||||
console.error('Storage error:', e);
|
||||
}
|
||||
},
|
||||
|
||||
remove(key) {
|
||||
localStorage.removeItem(key);
|
||||
}
|
||||
};
|
||||