add order status lookup for customers
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:
jamey
2026-02-24 08:40:08 +00:00
parent 4e36b654d3
commit 01ff8decd5
19 changed files with 1030 additions and 8 deletions

View File

@@ -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;
}
}

View File

@@ -281,6 +281,8 @@
border-radius: var(--t-radius-button);
border: none;
cursor: pointer;
padding: 0.75rem 1.5rem;
font-weight: 600;
}
& .themed-button-outline {
@@ -289,6 +291,8 @@
border: 1px solid var(--t-border-default);
border-radius: var(--t-radius-button);
cursor: pointer;
padding: 0.75rem 1.5rem;
font-weight: 600;
}
& .themed-card {