AbiirSkincare/Hydrogen/web/app/components/AccountOverview.tsx

190 lines
6.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import {Link} from 'react-router';
import {Money} from '@shopify/hydrogen';
import type {CustomerFragment} from 'customer-accountapi.generated';
import {AccountWishlistStat} from '~/components/AccountWishlistStat';
import {
getCustomerDisplayName,
getCustomerInitials,
} from '~/lib/customerDisplay';
export type AccountOverviewOrder = {
id: string;
name: string;
processedAt: string | null;
fulfillmentStatus: string | null;
totalPrice: {
amount: string;
currencyCode: string;
} | null;
lineItems: {
nodes: Array<{
title: string;
quantity: number;
price: {
amount: string;
currencyCode: string;
} | null;
}>;
} | null;
};
export type AccountOverviewData = {
creationDate: string | null;
orders: {
pageInfo: {hasNextPage: boolean};
nodes: AccountOverviewOrder[];
} | null;
};
function sumTotalSpent(orders: AccountOverviewOrder[], currency: string) {
let total = 0;
for (const o of orders) {
if (o.totalPrice && o.totalPrice.currencyCode === currency) {
total += Number.parseFloat(o.totalPrice.amount) || 0;
}
}
return total;
}
type Props = {
customer: CustomerFragment;
overview: AccountOverviewData;
};
/**
* Figma 123:11834 account overview: profile band, stat tiles, recent order.
*/
export function AccountOverview({customer, overview}: Props) {
const orders = overview.orders?.nodes ?? [];
const hasNext = overview.orders?.pageInfo.hasNextPage ?? false;
const count = orders.length;
const orderCountLabel = hasNext ? `${count}+` : String(count);
const spendCurrency = orders.find((o) => o.totalPrice)?.totalPrice
?.currencyCode;
const recent = orders[0] ?? null;
const memberLabel =
overview.creationDate ?
`Member since ${new Date(overview.creationDate).getFullYear()}`
: 'Member';
return (
<div className="flex flex-col gap-6">
<div className="flex flex-wrap items-center gap-4 rounded-sm bg-[#5d0b24] px-5 py-5 text-white">
<div
aria-hidden
className="flex h-16 w-16 shrink-0 items-center justify-center rounded-full border border-white/30 bg-white/10 font-['Raleway',sans-serif] text-lg font-medium uppercase tracking-wide"
>
{getCustomerInitials(customer)}
</div>
<div className="min-w-0">
<p className="font-['Raleway',sans-serif] text-base font-medium tracking-tight sm:text-lg">
{getCustomerDisplayName(customer) || 'Account'}
</p>
<p className="mt-0.5 font-['Raleway',sans-serif] text-sm text-white/85">
{memberLabel}
</p>
</div>
</div>
<ul className="grid grid-cols-1 gap-3 sm:grid-cols-3 sm:gap-4">
<AccountWishlistStat />
<li className="border border-[rgba(26,26,26,0.1)] bg-[#faf8f3] px-4 py-4 text-center sm:text-left">
<p className="font-['Raleway',sans-serif] text-xs uppercase tracking-[0.12em] text-[#5a5550]">
Orders
</p>
<p className="mt-1 font-['Raleway',sans-serif] text-xl font-medium text-[#1a1a18]">
{orderCountLabel}
</p>
</li>
<li className="border border-[rgba(26,26,26,0.1)] bg-[#faf8f3] px-4 py-4 text-center sm:text-left">
<p className="font-['Raleway',sans-serif] text-xs uppercase tracking-[0.12em] text-[#5a5550]">
Total spent
</p>
<div className="mt-1 font-['Raleway',sans-serif] text-xl font-medium text-[#1a1a18]">
{orders.length > 0 && spendCurrency ?
<Money
data={{
amount: String(sumTotalSpent(orders, spendCurrency)),
currencyCode: spendCurrency,
}}
/>
: '—'}
</div>
</li>
</ul>
<section className="border border-[rgba(26,26,26,0.1)] bg-white" aria-label="Recent order">
<div className=" px-4 pt-2 pb-3">
<h2 className="m-0 font-['Cormorant_Garamond',serif] text-[29.75px] font-light not-italic leading-[33.32px] tracking-[-0.446px] text-[#1A1A1A]">
Recent order
</h2>
</div>
{recent ?
<div className="px-4 py-4">
<div className="flex flex-col gap-1 sm:flex-row sm:items-baseline sm:justify-between">
<p className="font-['Raleway',sans-serif] text-base font-medium text-[#1a1a18]">
{recent.name}
</p>
<p className="font-['Raleway',sans-serif] text-sm text-[#5a5550]">
{recent.processedAt ?
new Date(recent.processedAt).toLocaleDateString()
: '—'}
</p>
</div>
{recent.fulfillmentStatus ?
<p className="mt-1 font-['Raleway',sans-serif] text-sm text-[#5a5550]">
{recent.fulfillmentStatus}
</p>
: null}
{recent.lineItems?.nodes.length ?
<ul className="mt-3 space-y-1 border-t border-[rgba(26,26,26,0.06)] pt-3">
{recent.lineItems.nodes.map((line, i) => (
<li
className="flex flex-wrap items-baseline justify-between gap-2 text-sm"
key={`${line.title}-${i}`}
>
<span className="font-['Raleway',sans-serif] text-[#1a1a18]">
{line.title}{' '}
<span className="text-[#5a5550]">×{line.quantity}</span>
</span>
{line.price ?
<span className="font-['Raleway',sans-serif] text-[#1a1a18]">
<Money data={line.price} />
</span>
: null}
</li>
))}
</ul>
: null}
{recent.totalPrice ?
<div className="mt-3 flex justify-end border-t border-[rgba(26,26,26,0.06)] pt-3 font-['Raleway',sans-serif] text-sm text-[#1a1a18]">
Total:{' '}
<span className="ml-1">
<Money data={recent.totalPrice} />
</span>
</div>
: null}
<p className="mt-4">
<Link
className="font-['Raleway',sans-serif] text-sm font-medium text-[#5d0b24] no-underline hover:opacity-80"
to={`/account/orders/${btoa(recent.id)}`}
>
View order
</Link>
</p>
</div>
: <p className="px-4 pt-10 pb-8 text-center font-['Raleway',sans-serif] text-sm text-[#5a5550]">
You haven&rsquo;t placed an order yet.{' '}
<Link className="text-[#5d0b24] no-underline hover:opacity-80" to="/collections/all">
Browse the shop
</Link>
</p>
}
</section>
</div>
);
}