add order status lookup for customers
All checks were successful
deploy / deploy (push) Successful in 1m17s
All checks were successful
deploy / deploy (push) Successful in 1m17s
Magic link flow on contact page: customer enters email, gets a time-limited signed link, clicks through to /orders showing all their paid orders and full detail pages with thumbnails and product links. - OrderLookupController generates/verifies Phoenix.Token signed links - Contact LiveView handles lookup_orders + reset_tracking events - Orders and OrderDetail LiveViews gated by session email - Order detail shows thumbnails, links to products still available - .themed-button gets base padding/font-weight so all usages are consistent - order-summary-card sticky scoped to .cart-grid (was leaking to orders list) - 27 new tests (1095 total) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1545,6 +1545,9 @@
|
||||
|
||||
.order-summary-card {
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
.cart-grid .order-summary-card {
|
||||
position: sticky;
|
||||
top: 1rem;
|
||||
}
|
||||
@@ -2217,6 +2220,7 @@
|
||||
|
||||
.checkout-item {
|
||||
display: flex;
|
||||
gap: 0.75rem;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
padding-bottom: 1rem;
|
||||
@@ -2228,11 +2232,29 @@
|
||||
}
|
||||
}
|
||||
|
||||
.checkout-item-thumb {
|
||||
width: 3.5rem;
|
||||
height: 3.5rem;
|
||||
object-fit: cover;
|
||||
border-radius: var(--t-radius-card);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.checkout-item > div {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.checkout-item-name {
|
||||
font-weight: 500;
|
||||
color: var(--t-text-primary);
|
||||
}
|
||||
|
||||
.checkout-item-link {
|
||||
text-decoration: none;
|
||||
|
||||
&:hover { text-decoration: underline; }
|
||||
}
|
||||
|
||||
.checkout-item-detail {
|
||||
font-size: var(--t-text-small, 0.875rem);
|
||||
color: var(--t-text-secondary);
|
||||
@@ -2517,3 +2539,235 @@
|
||||
color: var(--t-text-secondary);
|
||||
}
|
||||
}
|
||||
|
||||
@layer components {
|
||||
/* =========================================================
|
||||
Orders list page
|
||||
========================================================= */
|
||||
|
||||
.orders-main {
|
||||
max-width: 48rem;
|
||||
padding-block: 4rem;
|
||||
}
|
||||
|
||||
.orders-header {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.orders-page-title {
|
||||
font-family: var(--t-font-heading);
|
||||
font-size: var(--t-text-3xl, 1.875rem);
|
||||
font-weight: 700;
|
||||
color: var(--t-text-primary);
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.orders-email-label {
|
||||
color: var(--t-text-secondary);
|
||||
margin-bottom: 0.5rem;
|
||||
|
||||
& strong { color: var(--t-text-primary); }
|
||||
}
|
||||
|
||||
.orders-search-again {
|
||||
font-size: var(--t-text-small, 0.875rem);
|
||||
color: var(--t-accent);
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.orders-empty {
|
||||
color: var(--t-text-secondary);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.orders-empty-hint {
|
||||
font-size: var(--t-text-small, 0.875rem);
|
||||
}
|
||||
|
||||
.orders-contact-link {
|
||||
color: var(--t-accent);
|
||||
text-decoration: underline;
|
||||
font-size: var(--t-text-small, 0.875rem);
|
||||
}
|
||||
|
||||
.orders-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.order-summary-card {
|
||||
display: block;
|
||||
padding: 1.25rem 1.5rem;
|
||||
border: 1px solid var(--t-border-default);
|
||||
border-radius: var(--t-radius-card);
|
||||
background-color: var(--t-surface-card);
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
transition: border-color 0.15s ease, box-shadow 0.15s ease;
|
||||
|
||||
&:hover {
|
||||
border-color: var(--t-accent);
|
||||
box-shadow: 0 2px 8px rgb(0 0 0 / 0.08);
|
||||
}
|
||||
}
|
||||
|
||||
.order-summary-top {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
margin-bottom: 0.75rem;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.order-summary-number {
|
||||
font-weight: 600;
|
||||
color: var(--t-text-primary);
|
||||
font-size: var(--t-text-base, 1rem);
|
||||
}
|
||||
|
||||
.order-summary-date {
|
||||
font-size: var(--t-text-small, 0.875rem);
|
||||
color: var(--t-text-secondary);
|
||||
margin-top: 0.2rem;
|
||||
}
|
||||
|
||||
.order-summary-items {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0 0 1rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.25rem;
|
||||
}
|
||||
|
||||
.order-summary-item {
|
||||
font-size: var(--t-text-small, 0.875rem);
|
||||
color: var(--t-text-secondary);
|
||||
}
|
||||
|
||||
.order-summary-variant {
|
||||
color: var(--t-text-tertiary, var(--t-text-secondary));
|
||||
}
|
||||
|
||||
.order-summary-more {
|
||||
font-size: var(--t-text-small, 0.875rem);
|
||||
color: var(--t-text-tertiary, var(--t-text-secondary));
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.order-summary-footer {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
border-top: 1px solid var(--t-border-default);
|
||||
padding-top: 0.75rem;
|
||||
}
|
||||
|
||||
.order-summary-total {
|
||||
font-weight: 600;
|
||||
color: var(--t-text-primary);
|
||||
}
|
||||
|
||||
.order-summary-arrow {
|
||||
color: var(--t-text-secondary);
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
/* Status badge — shared between list + detail */
|
||||
.order-status-badge {
|
||||
display: inline-block;
|
||||
padding: 0.25rem 0.625rem;
|
||||
border-radius: 9999px;
|
||||
font-size: var(--t-text-xs, 0.75rem);
|
||||
font-weight: 600;
|
||||
white-space: nowrap;
|
||||
background-color: var(--t-surface-sunken);
|
||||
color: var(--t-text-secondary);
|
||||
|
||||
&--lg {
|
||||
font-size: var(--t-text-small, 0.875rem);
|
||||
padding: 0.375rem 0.875rem;
|
||||
margin-top: 0.75rem;
|
||||
}
|
||||
|
||||
&--shipped,
|
||||
&--delivered {
|
||||
background-color: color-mix(in srgb, var(--t-accent) 15%, transparent);
|
||||
color: var(--t-accent);
|
||||
}
|
||||
|
||||
&--failed {
|
||||
background-color: color-mix(in srgb, #ef4444 12%, transparent);
|
||||
color: #b91c1c;
|
||||
}
|
||||
}
|
||||
|
||||
/* =========================================================
|
||||
Order detail page
|
||||
========================================================= */
|
||||
|
||||
.order-detail-main {
|
||||
max-width: 48rem;
|
||||
padding-block: 4rem;
|
||||
}
|
||||
|
||||
.order-detail-header {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.order-detail-back {
|
||||
font-size: var(--t-text-small, 0.875rem);
|
||||
color: var(--t-accent);
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.order-detail-tracking-card {
|
||||
margin-bottom: 1.5rem;
|
||||
padding: 1.25rem 1.5rem;
|
||||
}
|
||||
|
||||
.order-detail-tracking {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
|
||||
& svg {
|
||||
flex-shrink: 0;
|
||||
color: var(--t-text-secondary);
|
||||
}
|
||||
}
|
||||
|
||||
.order-detail-tracking-label {
|
||||
font-size: var(--t-text-small, 0.875rem);
|
||||
font-weight: 600;
|
||||
color: var(--t-text-primary);
|
||||
}
|
||||
|
||||
.order-detail-tracking-number {
|
||||
font-size: var(--t-text-small, 0.875rem);
|
||||
color: var(--t-text-secondary);
|
||||
margin-top: 0.125rem;
|
||||
}
|
||||
|
||||
.order-detail-tracking-btn {
|
||||
margin-left: auto;
|
||||
flex-shrink: 0;
|
||||
padding: 0.5rem 1rem;
|
||||
font-size: var(--t-text-small, 0.875rem);
|
||||
}
|
||||
}
|
||||
|
||||
@layer components {
|
||||
.order-tracking-reset {
|
||||
background: none;
|
||||
border: none;
|
||||
padding: 0;
|
||||
cursor: pointer;
|
||||
font-size: var(--t-text-small, 0.875rem);
|
||||
color: var(--t-accent);
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user