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>
171 lines
5.6 KiB
TypeScript
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>
|
|
)
|
|
}
|