Article/PurchaseNav/DiscontinuedItem

Both Locales
English
Japanese

Component Source

discontinued-item.tsx

import type { FunctionComponent } from 'preact';
import type { PurchaseNavProduct } from '../types';
import { ProductImage } from '../shared/product-image';
import {
  formatProductFullTitle,
  getStatusMessages,
  BASE_CONTAINER_CLASS,
  TEXT_CONTENT_CONTAINER_CLASS,
} from '../constants';
import type { Locale } from '@/lib/i18n/types';

interface DiscontinuedItemProps {
  product: PurchaseNavProduct;
  locale?: Locale;
}

export const DiscontinuedItem: FunctionComponent<DiscontinuedItemProps> = ({
  product,
  locale = 'ja',
}) => {
  const { name, subtitle, imageSlug, imageMetadata, brandName } = product;
  const messages = getStatusMessages(locale);

  return (
    <div className={BASE_CONTAINER_CLASS}>
      <ProductImage
        imageSlug={imageSlug}
        imageMetadata={imageMetadata}
        brandName={brandName}
        productName={name}
        subtitle={subtitle}
      />
      <div className={TEXT_CONTENT_CONTAINER_CLASS}>
        <div className="text-sm sm:text-base md:text-lg">
          {formatProductFullTitle(brandName, name, subtitle)}
        </div>
        <div className="md:block">
          <div className="inline-block uppercase text-sm md:text-base sm:text-xl italic pr-[10px] mt-vgap-2xs">
            Discontinued.
          </div>
        </div>
        <div className="md:block">
          <div className="pt-[4px] text-xs md:text-sm hidden md:inline-block">
            {messages.discontinued}
          </div>
        </div>
        <div className="italic text-zd-subtext text-xs md:text-sm mt-vgap-2xs">
          {messages.discontinuedSpecsNote}
        </div>
      </div>
    </div>
  );
};

products.ts

import type { PurchaseNavProduct } from '../types';

/**
 * Mock brand display names for testing
 */
export const MOCK_BRAND_NAME = 'OAM';
export const MOCK_BRAND_NAME_TAKAZUDO = 'Takazudo Modular';

/**
 * Standard blurHash for mock product images
 * Used across all mock products for consistent loading placeholders
 */
export const MOCK_BLURHASH = 'LEHV6nWB2yk8pyo0adR*.7kCMdnj';

/**
 * Mock URL constants
 */
export const MOCK_URLS = {
  mercariProduct: 'https://mercari-shops.com/shops/mock-shop/items/mock-id',
  contact: '/contact/',
} as const;

/**
 * Creates a fully-typed mock product for testing.
 *
 * Mirrors the serializable shape produced at SSR by
 * `src/lib/resolve-purchase-nav-client-props.ts` (#476): brand name
 * pre-resolved, image slug pre-extracted, optional pre-resolved metadata.
 */
export const createMockProduct = (
  overrides: Partial<PurchaseNavProduct> = {},
): PurchaseNavProduct => ({
  slug: 'mock-product',
  name: 'Mock Product',
  brandName: MOCK_BRAND_NAME,
  imageSlug: 'oam-tiny-view1',
  imageMetadata: undefined,
  ...overrides,
});

/**
 * Pre-configured mock products for PurchaseNav status testing
 */
export const mockMercariProducts = {
  available: createMockProduct({
    slug: 'mock-available',
    name: 'Available Product',
    mercariProductId: 'mock-mercari-id-available',
    detailHref: '/products/mock-available-intro/',
    price: 153800,
  }),
  sold: createMockProduct({
    slug: 'mock-sold',
    name: 'Sold Out Product',
    mercariProductId: 'mock-mercari-id-sold',
    mercariStatus: 'sold',
    detailHref: '/products/mock-sold-intro/',
    price: 89000,
  }),
  incoming: createMockProduct({
    slug: 'mock-incoming',
    name: 'Incoming Product',
    mercariProductId: 'mock-mercari-id-incoming',
    mercariStatus: 'incoming',
    detailHref: '/products/mock-incoming-intro/',
    price: 45000,
  }),
  askToBuy: createMockProduct({
    slug: 'mock-ask-to-buy',
    name: 'Ask to Buy Product',
    mercariProductId: 'mock-mercari-id-asktobuy',
    mercariStatus: 'askToBuy',
    detailHref: '/products/mock-ask-to-buy-intro/',
    price: 275000,
  }),
  discontinued: createMockProduct({
    slug: 'mock-discontinued',
    name: 'Discontinued Product',
    mercariProductId: 'mock-mercari-id-discontinued',
    mercariStatus: 'discontinued',
    detailHref: '/products/mock-discontinued-intro/',
    price: 32000,
  }),
  unavailable: createMockProduct({
    slug: 'mock-unavailable',
    name: 'Unavailable Product',
    mercariProductId: 'mock-mercari-id-unavailable',
    mercariStatus: 'unavailable',
    detailHref: '/products/mock-unavailable-intro/',
    price: 28000,
  }),
};

/**
 * Pre-configured mock products for RelatedProducts testing
 */
export const mockRelatedProducts = {
  product1: createMockProduct({
    slug: 'mock-product-1',
    name: 'Mock Product One',
    detailHref: '/products/mock-product-1-intro/',
  }),
  product2: createMockProduct({
    slug: 'mock-product-2',
    name: 'Mock Product Two',
    detailHref: '/products/mock-product-2-intro/',
  }),
  product3: createMockProduct({
    slug: 'mock-product-3',
    name: 'Mock Product Three',
    brandName: MOCK_BRAND_NAME_TAKAZUDO,
    detailHref: '/products/mock-product-3-intro/',
  }),
  productNoDetail: createMockProduct({
    slug: 'mock-product-no-detail',
    name: 'Mock Product Without Detail',
    detailHref: undefined,
  }),
};

Story Source

discontinued-item.stories.tsx

import { DiscontinuedItem } from './status-items/discontinued-item';
import { mockMercariProducts } from './__mocks__/products';

/**
 * DiscontinuedItem Component Stories
 *
 * Renders the right-column notice for discontinued products:
 * - Italic uppercase "Discontinued." label (mirrors IncomingItem register)
 * - JA/EN status message
 * - Italic gray subline noting specs are preserved for reference
 */

export const meta = {
  title: 'Article/PurchaseNav/DiscontinuedItem',
};

const product = mockMercariProducts.discontinued;

/**
 * Japanese locale (default)
 */
export const Japanese = () => (
  <div className="font-futura bg-zd-dark p-vgap-sm">
    <DiscontinuedItem product={product} locale="ja" />
  </div>
);

/**
 * English locale
 */
export const English = () => (
  <div className="font-futura bg-zd-dark p-vgap-sm">
    <DiscontinuedItem product={product} locale="en" />
  </div>
);

/**
 * Both locales side by side
 */
export const BothLocales = () => (
  <div className="font-futura bg-zd-dark flex flex-col gap-vgap-sm p-vgap-sm">
    <div>
      <div className="text-zd-gray text-xs mb-vgap-2xs">JA</div>
      <DiscontinuedItem product={product} locale="ja" />
    </div>
    <div>
      <div className="text-zd-gray text-xs mb-vgap-2xs">EN</div>
      <DiscontinuedItem product={product} locale="en" />
    </div>
  </div>
);