Files
dashboards/apps/business/src/components/CategoryRow.tsx
T
Alireza HassaniandCursor 2a32c46b2e Add multilanguage catalog translations UI for products and categories.
Include AI translate-all with locale picker, count badges, MultiLanguage Data module gating, and shared modal action layout polish.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-04 09:51:11 +03:30

171 lines
5.6 KiB
TypeScript

import { FolderPlus, Globe, Layers, Trash2, ChevronRight, ClipboardList, Pencil } from 'lucide-react'
import { useLocale } from '@meshkee/dashboard-ui'
import type { Category } from '../types/category'
import { useT } from '../i18n/useT'
import { Tooltip } from './Tooltip'
import { useTenantBranding } from '../context/TenantBrandingContext'
import { hasBusinessModule } from '../utils/businessModules'
import styles from './CategoryRow.module.css'
interface CategoryRowProps {
category: Category
depth: number
hasChildren: boolean
expanded: boolean
variationCount: number
technicalFieldCount: number
onToggle: () => void
onAddSub: (id: string) => void
onVariations: (id: string) => void
onTechnicalForm: (id: string) => void
onEdit: (id: string) => void
onTranslate: (id: string) => void
onRemove: (id: string) => void
}
export function CategoryRow({
category,
depth,
hasChildren,
expanded,
variationCount,
technicalFieldCount,
onToggle,
onAddSub,
onVariations,
onTechnicalForm,
onEdit,
onTranslate,
onRemove,
}: CategoryRowProps) {
const t = useT()
const { locale } = useLocale()
const isFa = locale === 'fa'
const { enabledModules } = useTenantBranding()
const showTranslationControls = hasBusinessModule(enabledModules, 'multilanguage_data')
const translationCount = category.translationCount ?? 0
const primaryName = isFa ? category.nameFa || category.nameEn : category.nameEn
const secondaryName = isFa
? category.nameFa
? category.nameEn
: ''
: category.nameFa
return (
<div
className={styles.row}
style={{ marginInlineStart: depth * 8 }}
>
<div
className={`${styles.info} ${hasChildren ? styles.clickable : ''}`}
onClick={hasChildren ? onToggle : undefined}
onKeyDown={
hasChildren
? (e) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault()
onToggle()
}
}
: undefined
}
role={hasChildren ? 'button' : undefined}
tabIndex={hasChildren ? 0 : undefined}
aria-expanded={hasChildren ? expanded : undefined}
>
<div className={styles.textBlock}>
<div className={styles.names}>
<span className={isFa ? styles.nameFa : styles.nameEn}>{primaryName}</span>
{secondaryName ? (
<>
<span className={styles.separator}>·</span>
<span className={isFa ? styles.nameEn : styles.nameFa}>{secondaryName}</span>
</>
) : null}
{hasChildren && (
<span className={`${styles.chevron} ${expanded ? styles.chevronOpen : ''}`}>
<ChevronRight size={18} />
</span>
)}
</div>
{category.description && (
<p className={styles.description}>{category.description}</p>
)}
</div>
</div>
<div className={styles.controls}>
<Tooltip label={t('categories.tooltip.addSub')}>
<button
className={styles.controlBtn}
onClick={() => onAddSub(category.id)}
aria-label={t('categories.tooltip.addSub')}
>
<FolderPlus size={17} />
</button>
</Tooltip>
<Tooltip label={t('categories.tooltip.variations')}>
<button
className={`${styles.controlBtn} ${styles.iconBtn}`}
onClick={() => onVariations(category.id)}
aria-label={`${t('categories.tooltip.variations')} (${variationCount})`}
>
<Layers size={17} />
{variationCount > 0 && (
<span className={styles.variationBadge}>{variationCount}</span>
)}
</button>
</Tooltip>
<Tooltip label={t('categories.tooltip.technical')}>
<button
className={`${styles.controlBtn} ${styles.iconBtn}`}
onClick={() => onTechnicalForm(category.id)}
aria-label={`${t('categories.tooltip.technical')} (${technicalFieldCount})`}
>
<ClipboardList size={17} />
{technicalFieldCount > 0 && (
<span className={styles.technicalBadge}>{technicalFieldCount}</span>
)}
</button>
</Tooltip>
{showTranslationControls ? (
<Tooltip label={t('categories.tooltip.translate')}>
<button
className={`${styles.controlBtn} ${styles.iconBtn}`}
onClick={() => onTranslate(category.id)}
aria-label={
translationCount > 0
? `${t('categories.tooltip.translate')} (${translationCount})`
: t('categories.tooltip.translate')
}
>
<Globe size={17} />
{translationCount > 0 && (
<span className={styles.translateBadge}>{translationCount}</span>
)}
</button>
</Tooltip>
) : null}
<Tooltip label={t('categories.tooltip.edit')}>
<button
className={styles.controlBtn}
onClick={() => onEdit(category.id)}
aria-label={t('categories.tooltip.edit')}
>
<Pencil size={17} />
</button>
</Tooltip>
<Tooltip label={t('categories.tooltip.remove')}>
<button
className={`${styles.controlBtn} ${styles.danger}`}
onClick={() => onRemove(category.id)}
aria-label={t('categories.tooltip.remove')}
>
<Trash2 size={17} />
</button>
</Tooltip>
</div>
</div>
)
}