add shadcn ui

This commit is contained in:
Nông Đức Huy
2026-08-13 23:20:23 +07:00
parent 57f44796ce
commit fee9523b35
50 changed files with 1331 additions and 211 deletions
@@ -5,7 +5,7 @@ import Image from 'next/image';
import { useFormatter, useTranslations } from 'next-intl';
import { CART_NOTICE_REASONS } from '@sport/types';
import { Button, Skeleton } from '@sport/ui';
import { Button, FormError, Skeleton } from '@sport/ui';
import { useCart } from '@/features/cart/cart-provider';
import { Link } from '@/i18n/navigation';
@@ -76,11 +76,7 @@ export function CartView() {
</ul>
) : null}
{error ? (
<p role="alert" className="text-danger text-sm">
{error}
</p>
) : null}
<FormError message={error} />
<ul className="divide-ink-200 divide-y">
{cart.lines.map((line) => (
@@ -6,7 +6,7 @@ import { useState } from 'react';
import { isApiClientError, type ProductListQuery } from '@sport/api-client';
import type { Locale, ProductListItem } from '@sport/types';
import { Button } from '@sport/ui';
import { Button, FormError } from '@sport/ui';
import { Link } from '@/i18n/navigation';
import { browserApi } from '@/lib/api';
@@ -88,11 +88,7 @@ export function LoadMore({
{t('showing', { shown, total: totalCount })}
</p>
{error ? (
<p role="alert" className="text-danger text-sm">
{error}
</p>
) : null}
<FormError message={error} />
{cursor ? (
<Button variant="outline" size="lg" asChild>
@@ -6,7 +6,7 @@ import { useEffect, useState } from 'react';
import { isApiClientError } from '@sport/api-client';
import type { Locale, ReviewableItem } from '@sport/types';
import { Button, Input, cn } from '@sport/ui';
import { Button, FormError, Input, Textarea, cn } from '@sport/ui';
import { browserApi } from '@/lib/api';
@@ -155,21 +155,16 @@ function ItemReview({
aria-label={t('titlePlaceholder')}
maxLength={140}
/>
<textarea
<Textarea
value={body}
onChange={(event) => setBody(event.target.value)}
placeholder={t('bodyPlaceholder')}
aria-label={t('bodyPlaceholder')}
maxLength={2000}
rows={3}
className="border-ink-200 focus:border-ink-950 w-full border px-3 py-2 text-sm outline-none"
/>
{error ? (
<p role="alert" className="text-danger text-xs">
{error}
</p>
) : null}
<FormError message={error} />
<Button size="sm" disabled={saving || !authorName.trim()} onClick={submit}>
{saving ? t('sending') : t('submit')}
@@ -12,7 +12,7 @@ import type {
OrderListItem,
ProductListItem,
} from '@sport/types';
import { Badge, Button, EmptyState, Input, Skeleton } from '@sport/ui';
import { Badge, Button, EmptyState, FormError, Input, Skeleton } from '@sport/ui';
import { Link } from '@/i18n/navigation';
import { browserApi } from '@/lib/api';
@@ -115,11 +115,7 @@ export function AccountOrders() {
<div className="space-y-6">
<h1 className="text-2xl font-black uppercase tracking-tight">{t('nav.orders')}</h1>
{error ? (
<p role="alert" className="text-danger text-sm">
{error}
</p>
) : null}
<FormError message={error} />
{!data ? (
<Skeleton className="h-40 w-full" />
@@ -244,11 +240,7 @@ export function AccountAddresses() {
) : null}
</div>
{error ? (
<p role="alert" className="text-danger text-sm">
{error}
</p>
) : null}
<FormError message={error} />
{draft ? (
<form onSubmit={save} className="border-ink-200 space-y-4 border p-5">
@@ -301,11 +293,7 @@ export function AccountAddresses() {
<span className="text-sm">{t('makeDefault')}</span>
</label>
{formError ? (
<p role="alert" className="text-danger text-sm">
{formError}
</p>
) : null}
<FormError message={formError} />
<div className="flex gap-2">
<Button type="submit" size="sm" disabled={busy}>
@@ -469,11 +457,7 @@ export function AccountProfile() {
<span className="text-ink-600 text-sm">{t('marketing')}</span>
</label>
{formError ? (
<p role="alert" className="text-danger text-sm">
{formError}
</p>
) : null}
<FormError message={formError} />
{saved ? <p className="text-success text-sm">{t('saved')}</p> : null}
<Button type="submit" disabled={busy}>
@@ -517,11 +501,7 @@ export function AccountWishlist() {
<div className="space-y-6">
<h1 className="text-2xl font-black uppercase tracking-tight">{t('nav.wishlist')}</h1>
{error ? (
<p role="alert" className="text-danger text-sm">
{error}
</p>
) : null}
<FormError message={error} />
{!data ? (
<Skeleton className="h-32 w-full" />
@@ -5,7 +5,7 @@ import { useTranslations } from 'next-intl';
import { useState, type FormEvent } from 'react';
import { isApiClientError } from '@sport/api-client';
import { Button, Input } from '@sport/ui';
import { Button, Checkbox, FormError, Input } from '@sport/ui';
import { Link } from '@/i18n/navigation';
import { routes } from '@/lib/routes';
@@ -152,22 +152,16 @@ export function AuthForm({ mode }: { mode: 'signIn' | 'register' }) {
</label>
<label className="flex items-start gap-2.5">
<input
type="checkbox"
<Checkbox
checked={acceptsMarketing}
onChange={(event) => setAcceptsMarketing(event.target.checked)}
className="border-ink-300 accent-ink-950 mt-0.5 size-4"
onCheckedChange={(checked) => setAcceptsMarketing(checked === true)}
/>
<span className="text-ink-600 text-sm">{t('marketing')}</span>
</label>
</>
) : null}
{error ? (
<p role="alert" className="text-danger text-sm">
{error}
</p>
) : null}
<FormError message={error} />
<Button type="submit" disabled={busy} className="w-full">
{busy ? t('working') : isRegister ? t('createAccount') : t('signIn')}