DashboardPage.tsx 6.15 KB
import { useEffect, useState } from 'react'
import { Link } from 'react-router-dom'
import {
  catalog,
  finance,
  inventory,
  partners,
  production,
  purchaseOrders,
  salesOrders,
} from '@/api/client'
import { PageHeader } from '@/components/PageHeader'
import { Loading } from '@/components/Loading'
import { ErrorBox } from '@/components/ErrorBox'
import { useAuth } from '@/auth/AuthContext'
import { useT } from '@/i18n/LocaleContext'

interface DashboardCounts {
  items: number
  partners: number
  locations: number
  salesOrders: number
  purchaseOrders: number
  workOrders: number
  journalEntries: number
  inProgressWorkOrders: number
}

export function DashboardPage() {
  const { username } = useAuth()
  const t = useT()
  const [counts, setCounts] = useState<DashboardCounts | null>(null)
  const [error, setError] = useState<Error | null>(null)
  const [loading, setLoading] = useState(true)

  useEffect(() => {
    let active = true
    setLoading(true)
    Promise.all([
      catalog.listItems(),
      partners.list(),
      inventory.listLocations(),
      salesOrders.list(),
      purchaseOrders.list(),
      production.listWorkOrders(),
      finance.listJournalEntries(),
      production.shopFloor(),
    ])
      .then(([items, parts, locs, sos, pos, wos, jes, sf]) => {
        if (!active) return
        setCounts({
          items: items.length,
          partners: parts.length,
          locations: locs.length,
          salesOrders: sos.length,
          purchaseOrders: pos.length,
          workOrders: wos.length,
          journalEntries: jes.length,
          inProgressWorkOrders: sf.length,
        })
      })
      .catch((e: unknown) => {
        if (active) setError(e instanceof Error ? e : new Error(String(e)))
      })
      .finally(() => active && setLoading(false))
    return () => {
      active = false
    }
  }, [])

  return (
    <div>
      <PageHeader
        title={`${t('page.dashboard.welcome')}${username ? ', ' + username : ''}`}
        subtitle={t('page.dashboard.subtitle')}
      />
      {loading && <Loading />}
      {error && <ErrorBox error={error} />}
      {counts && (
        <div className="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4">
          <DashboardCard label={t('page.dashboard.cardItems')} value={counts.items} to="/items" />
          <DashboardCard label={t('page.dashboard.cardPartners')} value={counts.partners} to="/partners" />
          <DashboardCard label={t('page.dashboard.cardLocations')} value={counts.locations} to="/locations" />
          <DashboardCard
            label={t('page.dashboard.cardWoInProgress')}
            value={counts.inProgressWorkOrders}
            to="/shop-floor"
            highlight
          />
          <DashboardCard label={t('page.dashboard.cardSalesOrders')} value={counts.salesOrders} to="/sales-orders" />
          <DashboardCard
            label={t('page.dashboard.cardPurchaseOrders')}
            value={counts.purchaseOrders}
            to="/purchase-orders"
          />
          <DashboardCard label={t('page.dashboard.cardWorkOrders')} value={counts.workOrders} to="/work-orders" />
          <DashboardCard
            label={t('page.dashboard.cardJournalEntries')}
            value={counts.journalEntries}
            to="/journal-entries"
          />
        </div>
      )}
      <div className="mt-8 card p-5 text-sm text-slate-600">
        <h2 className="mb-2 text-base font-semibold text-slate-800">{t('page.dashboard.gettingStarted')}</h2>
        <p className="mb-3 text-slate-500">
          {t('page.dashboard.gettingStartedDesc')}
        </p>
        <ol className="list-decimal space-y-2 pl-5">
          <li>
            <strong>{t('page.dashboard.step1')}</strong> — {t('page.dashboard.step1Desc')}{' '}
            <Link to="/items/new" className="text-brand-600 hover:underline">{t('page.dashboard.step1DescItems')}</Link>,{' '}
            <Link to="/partners/new" className="text-brand-600 hover:underline">{t('page.dashboard.step1DescPartners')}</Link>{t('page.dashboard.step1DescAnd')}{' '}
            <Link to="/locations/new" className="text-brand-600 hover:underline">{t('page.dashboard.step1DescLocations')}</Link>{t('page.dashboard.step1DescThen')}{' '}
            <Link to="/balances/adjust" className="text-brand-600 hover:underline">{t('page.dashboard.step1DescAdjust')}</Link>{' '}
            {t('page.dashboard.step1DescEnd')}
          </li>
          <li>
            <strong>{t('page.dashboard.step2')}</strong> —{' '}
            <Link to="/sales-orders/new" className="text-brand-600 hover:underline">{t('page.dashboard.step2Link')}</Link>{' '}
            {t('page.dashboard.step2Desc')}
          </li>
          <li>
            <strong>{t('page.dashboard.step3')}</strong> — {t('page.dashboard.step3Desc1')}{' '}
            <Link to="/shop-floor" className="text-brand-600 hover:underline">{t('page.dashboard.step3ShopFloor')}</Link>{t('page.dashboard.step3Desc2')}
          </li>
          <li>
            <strong>{t('page.dashboard.step4')}</strong> — {t('page.dashboard.step4Desc1')}{' '}
            <Link to="/movements" className="text-brand-600 hover:underline">{t('page.dashboard.step4Movements')}</Link>{' '}
            {t('page.dashboard.step4Desc2')}{' '}
            <Link to="/journal-entries" className="text-brand-600 hover:underline">{t('page.dashboard.step4JE')}</Link>.
          </li>
          <li>
            <strong>{t('page.dashboard.step5')}</strong> — {t('page.dashboard.step5Desc1')}{' '}
            <Link to="/purchase-orders/new" className="text-brand-600 hover:underline">{t('page.dashboard.step5Link')}</Link>{t('page.dashboard.step5Desc2')}
          </li>
        </ol>
      </div>
    </div>
  )
}

function DashboardCard({
  label,
  value,
  to,
  highlight = false,
}: {
  label: string
  value: number
  to: string
  highlight?: boolean
}) {
  return (
    <Link
      to={to}
      className={[
        'card flex flex-col gap-1 p-5 transition hover:shadow-md',
        highlight ? 'ring-2 ring-brand-200' : '',
      ].join(' ')}
    >
      <div className="text-xs font-semibold uppercase tracking-wide text-slate-500">{label}</div>
      <div className="text-3xl font-bold text-slate-900">{value}</div>
    </Link>
  )
}