SchedulerView ve TodoBoard komponenti useListFormColumns ile çalıştırıldı
This commit is contained in:
parent
3a03c8e1e8
commit
f8dcf25b26
3 changed files with 139 additions and 61 deletions
|
|
@ -19743,8 +19743,8 @@
|
||||||
{
|
{
|
||||||
"resourceName": "Platform",
|
"resourceName": "Platform",
|
||||||
"key": "ListForms.TodoBoard.Status",
|
"key": "ListForms.TodoBoard.Status",
|
||||||
"en": "Kanban / Status",
|
"en": "Status",
|
||||||
"tr": "Kanban / Durum"
|
"tr": "Durum"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"resourceName": "Platform",
|
"resourceName": "Platform",
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,6 @@ import {
|
||||||
FieldCustomValueTypeEnum,
|
FieldCustomValueTypeEnum,
|
||||||
GridDto,
|
GridDto,
|
||||||
PlatformEditorTypes,
|
PlatformEditorTypes,
|
||||||
UiLookupDataSourceTypeEnum,
|
|
||||||
} from '@/proxy/form/models'
|
} from '@/proxy/form/models'
|
||||||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||||
import Scheduler, {
|
import Scheduler, {
|
||||||
|
|
@ -38,6 +37,8 @@ import type { ValidationRule } from 'devextreme/common'
|
||||||
import { FaAlignLeft, FaClock, FaHeading } from 'react-icons/fa'
|
import { FaAlignLeft, FaClock, FaHeading } from 'react-icons/fa'
|
||||||
import { Avatar } from '@/components/ui'
|
import { Avatar } from '@/components/ui'
|
||||||
import { getUsers } from '@/services/identity.service'
|
import { getUsers } from '@/services/identity.service'
|
||||||
|
import { useListFormColumns } from './useListFormColumns'
|
||||||
|
import type { GridColumnData } from './GridColumnData'
|
||||||
|
|
||||||
interface SchedulerViewProps {
|
interface SchedulerViewProps {
|
||||||
listFormCode: string
|
listFormCode: string
|
||||||
|
|
@ -82,6 +83,11 @@ const getValueByField = (data: Record<string, any>, field?: string) => {
|
||||||
return resolvedField ? data[resolvedField] : undefined
|
return resolvedField ? data[resolvedField] : undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const flattenColumns = (columns: GridColumnData[] = []): GridColumnData[] =>
|
||||||
|
columns.flatMap((column) =>
|
||||||
|
column.columns?.length ? flattenColumns(column.columns as GridColumnData[]) : [column],
|
||||||
|
)
|
||||||
|
|
||||||
const SchedulerView = (props: SchedulerViewProps) => {
|
const SchedulerView = (props: SchedulerViewProps) => {
|
||||||
const { listFormCode, searchParams, isSubForm, level, gridDto: extGridDto } = props
|
const { listFormCode, searchParams, isSubForm, level, gridDto: extGridDto } = props
|
||||||
const { translate } = useLocalization()
|
const { translate } = useLocalization()
|
||||||
|
|
@ -158,6 +164,13 @@ const SchedulerView = (props: SchedulerViewProps) => {
|
||||||
}, [listFormCode])
|
}, [listFormCode])
|
||||||
|
|
||||||
const { createSelectDataSource } = useListFormCustomDataSource({ gridRef: schedulerRef })
|
const { createSelectDataSource } = useListFormCustomDataSource({ gridRef: schedulerRef })
|
||||||
|
const { getBandedColumns } = useListFormColumns({
|
||||||
|
gridDto,
|
||||||
|
listFormCode,
|
||||||
|
isSubForm,
|
||||||
|
gridRef: schedulerRef,
|
||||||
|
})
|
||||||
|
const listFormColumns = useMemo(() => flattenColumns(getBandedColumns()), [getBandedColumns])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!gridDto) {
|
if (!gridDto) {
|
||||||
|
|
@ -643,8 +656,10 @@ const SchedulerView = (props: SchedulerViewProps) => {
|
||||||
console.log(err)
|
console.log(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
const fieldName = i.fieldName!
|
|
||||||
const listFormField = i
|
const listFormField = i
|
||||||
|
const listFormColumn = listFormColumns.find(
|
||||||
|
(column) => column.dataField?.toLowerCase() === i.fieldName?.toLowerCase(),
|
||||||
|
)
|
||||||
|
|
||||||
if (listFormField?.sourceDbType === DbTypeEnum.Date) {
|
if (listFormField?.sourceDbType === DbTypeEnum.Date) {
|
||||||
editorOptions = {
|
editorOptions = {
|
||||||
|
|
@ -678,48 +693,14 @@ const SchedulerView = (props: SchedulerViewProps) => {
|
||||||
editorType = i.editorType2
|
editorType = i.editorType2
|
||||||
}
|
}
|
||||||
|
|
||||||
// Lookup DataSource oluştur
|
const lookup = listFormColumn?.lookup
|
||||||
if (listFormField?.lookupDto) {
|
if (lookup) {
|
||||||
const lookup = listFormField.lookupDto
|
editorOptions.dataSource =
|
||||||
|
typeof lookup.dataSource === 'function'
|
||||||
if (lookup.dataSourceType === UiLookupDataSourceTypeEnum.Query) {
|
? lookup.dataSource({ data: e.appointmentData ?? {} })
|
||||||
editorOptions.dataSource = new CustomStore({
|
: lookup.dataSource
|
||||||
key: 'key',
|
editorOptions.valueExpr = lookup.valueExpr
|
||||||
loadMode: 'raw',
|
editorOptions.displayExpr = lookup.displayExpr
|
||||||
load: async () => {
|
|
||||||
try {
|
|
||||||
const { dynamicFetch } = await import('@/services/form.service')
|
|
||||||
const response = await dynamicFetch('list-form-select/lookup', 'POST', null, {
|
|
||||||
listFormCode,
|
|
||||||
listFormFieldName: fieldName,
|
|
||||||
filters: [],
|
|
||||||
})
|
|
||||||
return (response.data ?? []).map((a: any) => ({
|
|
||||||
key: a.Key,
|
|
||||||
name: a.Name,
|
|
||||||
group: a.Group,
|
|
||||||
}))
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Lookup load error:', error)
|
|
||||||
return []
|
|
||||||
}
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
editorOptions.valueExpr = 'key'
|
|
||||||
editorOptions.displayExpr = 'name'
|
|
||||||
} else if (lookup.dataSourceType === UiLookupDataSourceTypeEnum.StaticData) {
|
|
||||||
if (lookup.lookupQuery) {
|
|
||||||
try {
|
|
||||||
const staticData = JSON.parse(lookup.lookupQuery)
|
|
||||||
editorOptions.dataSource = staticData
|
|
||||||
editorOptions.valueExpr = lookup.valueExpr || 'key'
|
|
||||||
editorOptions.displayExpr = lookup.displayExpr || 'name'
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Static data parse error:', error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const item: SimpleItemWithColData = {
|
const item: SimpleItemWithColData = {
|
||||||
|
|
@ -822,7 +803,7 @@ const SchedulerView = (props: SchedulerViewProps) => {
|
||||||
e.form.option('showValidationSummary', false)
|
e.form.option('showValidationSummary', false)
|
||||||
e.form.option('items', result)
|
e.form.option('items', result)
|
||||||
},
|
},
|
||||||
[gridDto, translate, isPopupFullScreen, listFormCode, useMobileEditPopup],
|
[gridDto, translate, isPopupFullScreen, listFormColumns, useMobileEditPopup],
|
||||||
)
|
)
|
||||||
|
|
||||||
const configuredEditPopup = gridDto?.gridOptions.editingOptionDto?.popup
|
const configuredEditPopup = gridDto?.gridOptions.editingOptionDto?.popup
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,9 @@ import { getTenants } from '@/services/tenant.service'
|
||||||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||||
import { usePermission } from '@/utils/hooks/usePermission'
|
import { usePermission } from '@/utils/hooks/usePermission'
|
||||||
import { usePWA } from '@/utils/hooks/usePWA'
|
import { usePWA } from '@/utils/hooks/usePWA'
|
||||||
|
import { useListFormColumns } from './useListFormColumns'
|
||||||
|
import type { GridColumnData } from './GridColumnData'
|
||||||
|
import { useStoreState } from '@/store/store'
|
||||||
|
|
||||||
dayjs.extend(relativeTime)
|
dayjs.extend(relativeTime)
|
||||||
|
|
||||||
|
|
@ -78,6 +81,13 @@ interface UserOption {
|
||||||
avatarUrl?: string
|
avatarUrl?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type LookupDisplayValues = Record<string, Map<string, string>>
|
||||||
|
|
||||||
|
const flattenColumns = (columns: GridColumnData[] = []): GridColumnData[] =>
|
||||||
|
columns.flatMap((column) =>
|
||||||
|
column.columns?.length ? flattenColumns(column.columns as GridColumnData[]) : [column],
|
||||||
|
)
|
||||||
|
|
||||||
const fieldValue = (row: TodoRow, field?: string) => (field ? row[field] : undefined)
|
const fieldValue = (row: TodoRow, field?: string) => (field ? row[field] : undefined)
|
||||||
|
|
||||||
const tenantIdValue = (row: TodoRow) => row.TenantId ?? row.tenantId
|
const tenantIdValue = (row: TodoRow) => row.TenantId ?? row.tenantId
|
||||||
|
|
@ -157,7 +167,7 @@ const priorityLabel = (
|
||||||
: translate('::ListForms.TodoBoard.NoPriority')
|
: translate('::ListForms.TodoBoard.NoPriority')
|
||||||
}
|
}
|
||||||
|
|
||||||
const TodoBoard = ({ listFormCode, searchParams, gridDto }: TodoBoardProps) => {
|
const TodoBoard = ({ listFormCode, searchParams, isSubForm, gridDto }: TodoBoardProps) => {
|
||||||
const options = gridDto.gridOptions.todoOptionDto as TodoOptionDto
|
const options = gridDto.gridOptions.todoOptionDto as TodoOptionDto
|
||||||
const editingOptions = gridDto.gridOptions.editingOptionDto
|
const editingOptions = gridDto.gridOptions.editingOptionDto
|
||||||
const canAdd =
|
const canAdd =
|
||||||
|
|
@ -167,9 +177,40 @@ const TodoBoard = ({ listFormCode, searchParams, gridDto }: TodoBoardProps) => {
|
||||||
const canDelete =
|
const canDelete =
|
||||||
editingOptions?.allowDeleting === true && Boolean(gridDto.gridOptions.deleteServiceAddress)
|
editingOptions?.allowDeleting === true && Boolean(gridDto.gridOptions.deleteServiceAddress)
|
||||||
const keyField = gridDto.gridOptions.keyFieldName ?? 'id'
|
const keyField = gridDto.gridOptions.keyFieldName ?? 'id'
|
||||||
|
const userNameField =
|
||||||
|
gridDto.columnFormats?.find((column) => column.fieldName?.toLowerCase() === 'username')
|
||||||
|
?.fieldName ?? 'UserName'
|
||||||
const stateStoring = gridDto.gridOptions.stateStoringDto
|
const stateStoring = gridDto.gridOptions.stateStoringDto
|
||||||
|
const currentUserName = useStoreState((state) => state.auth.user.userName)
|
||||||
const gridRef = useRef<DataGridRef<any, any>>()
|
const gridRef = useRef<DataGridRef<any, any>>()
|
||||||
const { createSelectDataSource } = useListFormCustomDataSource({ gridRef })
|
const { createSelectDataSource } = useListFormCustomDataSource({ gridRef })
|
||||||
|
const { getBandedColumns, loadLookupDisplayValues } = useListFormColumns({
|
||||||
|
gridDto,
|
||||||
|
listFormCode,
|
||||||
|
isSubForm,
|
||||||
|
gridRef,
|
||||||
|
})
|
||||||
|
const getBandedColumnsRef = useRef(getBandedColumns)
|
||||||
|
const loadLookupDisplayValuesRef = useRef(loadLookupDisplayValues)
|
||||||
|
getBandedColumnsRef.current = getBandedColumns
|
||||||
|
loadLookupDisplayValuesRef.current = loadLookupDisplayValues
|
||||||
|
const listFormColumnsCacheRef = useRef<{
|
||||||
|
gridDto?: GridDto
|
||||||
|
listFormCode: string
|
||||||
|
columns: GridColumnData[]
|
||||||
|
}>()
|
||||||
|
if (
|
||||||
|
!listFormColumnsCacheRef.current ||
|
||||||
|
listFormColumnsCacheRef.current.gridDto !== gridDto ||
|
||||||
|
listFormColumnsCacheRef.current.listFormCode !== listFormCode
|
||||||
|
) {
|
||||||
|
listFormColumnsCacheRef.current = {
|
||||||
|
gridDto,
|
||||||
|
listFormCode,
|
||||||
|
columns: flattenColumns(getBandedColumnsRef.current()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const listFormColumns = listFormColumnsCacheRef.current.columns
|
||||||
const [store, setStore] = useState<CustomStore<any, any>>()
|
const [store, setStore] = useState<CustomStore<any, any>>()
|
||||||
const [rows, setRows] = useState<TodoRow[]>([])
|
const [rows, setRows] = useState<TodoRow[]>([])
|
||||||
const [statuses, setStatuses] = useState<string[]>([])
|
const [statuses, setStatuses] = useState<string[]>([])
|
||||||
|
|
@ -194,6 +235,7 @@ const TodoBoard = ({ listFormCode, searchParams, gridDto }: TodoBoardProps) => {
|
||||||
const [userOptions, setUserOptions] = useState<UserOption[]>([])
|
const [userOptions, setUserOptions] = useState<UserOption[]>([])
|
||||||
const [tenantNames, setTenantNames] = useState<Map<string, string>>(new Map())
|
const [tenantNames, setTenantNames] = useState<Map<string, string>>(new Map())
|
||||||
const [usersLoading, setUsersLoading] = useState(false)
|
const [usersLoading, setUsersLoading] = useState(false)
|
||||||
|
const [lookupDisplayValues, setLookupDisplayValues] = useState<LookupDisplayValues>({})
|
||||||
const columnConfigRef = useRef<StoredBoardColumns>({ columns: [], retired: [] })
|
const columnConfigRef = useRef<StoredBoardColumns>({ columns: [], retired: [] })
|
||||||
const dragActiveRef = useRef(false)
|
const dragActiveRef = useRef(false)
|
||||||
const { translate } = useLocalization()
|
const { translate } = useLocalization()
|
||||||
|
|
@ -201,6 +243,23 @@ const TodoBoard = ({ listFormCode, searchParams, gridDto }: TodoBoardProps) => {
|
||||||
const isPwaMode = usePWA()
|
const isPwaMode = usePWA()
|
||||||
const uncategorized = translate('::ListForms.TodoBoard.Uncategorized')
|
const uncategorized = translate('::ListForms.TodoBoard.Uncategorized')
|
||||||
|
|
||||||
|
const lookupValuesForField = useCallback(
|
||||||
|
(field?: string) => {
|
||||||
|
if (!field) return undefined
|
||||||
|
const dataField = listFormColumns.find(
|
||||||
|
(column) => column.dataField?.toLowerCase() === field.toLowerCase(),
|
||||||
|
)?.dataField
|
||||||
|
return dataField ? lookupDisplayValues[dataField] : undefined
|
||||||
|
},
|
||||||
|
[listFormColumns, lookupDisplayValues],
|
||||||
|
)
|
||||||
|
|
||||||
|
const lookupText = useCallback(
|
||||||
|
(field: string | undefined, value: unknown) =>
|
||||||
|
lookupValuesForField(field)?.get(String(value)) ?? String(value ?? ''),
|
||||||
|
[lookupValuesForField],
|
||||||
|
)
|
||||||
|
|
||||||
const clearDragState = useCallback(() => {
|
const clearDragState = useCallback(() => {
|
||||||
dragActiveRef.current = false
|
dragActiveRef.current = false
|
||||||
setDraggedKey(undefined)
|
setDraggedKey(undefined)
|
||||||
|
|
@ -267,6 +326,22 @@ const TodoBoard = ({ listFormCode, searchParams, gridDto }: TodoBoardProps) => {
|
||||||
}
|
}
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
let active = true
|
||||||
|
setLookupDisplayValues({})
|
||||||
|
|
||||||
|
loadLookupDisplayValuesRef
|
||||||
|
.current(listFormColumns)
|
||||||
|
.then((values) => {
|
||||||
|
if (active) setLookupDisplayValues(values)
|
||||||
|
})
|
||||||
|
.catch((error) => console.error('TodoBoard lookup load error:', error))
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
active = false
|
||||||
|
}
|
||||||
|
}, [listFormColumns])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let cancelled = false
|
let cancelled = false
|
||||||
|
|
||||||
|
|
@ -384,14 +459,23 @@ const TodoBoard = ({ listFormCode, searchParams, gridDto }: TodoBoardProps) => {
|
||||||
[options.assigneeExpr, rows],
|
[options.assigneeExpr, rows],
|
||||||
)
|
)
|
||||||
const assigneeOptions = useMemo<UserOption[]>(() => {
|
const assigneeOptions = useMemo<UserOption[]>(() => {
|
||||||
const knownUsers = new Set(userOptions.map((option) => option.value))
|
const lookupValues = lookupValuesForField(options.assigneeExpr)
|
||||||
|
const avatarUrls = new Map(userOptions.map((option) => [option.value, option.avatarUrl]))
|
||||||
|
const configuredOptions = lookupValues
|
||||||
|
? Array.from(lookupValues, ([value, label]) => ({
|
||||||
|
value,
|
||||||
|
label,
|
||||||
|
avatarUrl: avatarUrls.get(value),
|
||||||
|
}))
|
||||||
|
: userOptions
|
||||||
|
const knownUsers = new Set(configuredOptions.map((option) => option.value))
|
||||||
return [
|
return [
|
||||||
...userOptions,
|
...configuredOptions,
|
||||||
...assigneeSuggestions
|
...assigneeSuggestions
|
||||||
.filter((value) => !knownUsers.has(value))
|
.filter((value) => !knownUsers.has(value))
|
||||||
.map((value) => ({ value, label: value })),
|
.map((value) => ({ value, label: value })),
|
||||||
]
|
]
|
||||||
}, [assigneeSuggestions, userOptions])
|
}, [assigneeSuggestions, lookupValuesForField, options.assigneeExpr, userOptions])
|
||||||
const assigneeOptionMap = useMemo(
|
const assigneeOptionMap = useMemo(
|
||||||
() => new Map(assigneeOptions.map((option) => [option.value, option])),
|
() => new Map(assigneeOptions.map((option) => [option.value, option])),
|
||||||
[assigneeOptions],
|
[assigneeOptions],
|
||||||
|
|
@ -681,6 +765,7 @@ const TodoBoard = ({ listFormCode, searchParams, gridDto }: TodoBoardProps) => {
|
||||||
const values: Record<string, any> = {
|
const values: Record<string, any> = {
|
||||||
[options.titleExpr]: title,
|
[options.titleExpr]: title,
|
||||||
[options.statusExpr]: status,
|
[options.statusExpr]: status,
|
||||||
|
[userNameField]: currentUserName,
|
||||||
}
|
}
|
||||||
if (options.completedExpr) values[options.completedExpr] = false
|
if (options.completedExpr) values[options.completedExpr] = false
|
||||||
if (options.orderExpr) values[options.orderExpr] = itemsForStatus(status).length
|
if (options.orderExpr) values[options.orderExpr] = itemsForStatus(status).length
|
||||||
|
|
@ -778,12 +863,11 @@ const TodoBoard = ({ listFormCode, searchParams, gridDto }: TodoBoardProps) => {
|
||||||
<section className="min-h-[420px] rounded-xl shadow-sm">
|
<section className="min-h-[420px] rounded-xl shadow-sm">
|
||||||
{renderToolbar(true)}
|
{renderToolbar(true)}
|
||||||
<div className="mb-6 flex justify-end border-b border-gray-200 dark:border-gray-700">
|
<div className="mb-6 flex justify-end border-b border-gray-200 dark:border-gray-700">
|
||||||
{editingOptions?.allowUpdating === true &&
|
{editingOptions?.allowUpdating === true && !gridDto.gridOptions.updateServiceAddress && (
|
||||||
!gridDto.gridOptions.updateServiceAddress && (
|
<span className="text-xs text-amber-600">
|
||||||
<span className="text-xs text-amber-600">
|
{translate('::ListForms.TodoBoard.EditRequiresUpdateService')}
|
||||||
{translate('::ListForms.TodoBoard.EditRequiresUpdateService')}
|
</span>
|
||||||
</span>
|
)}
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
<div className="grid gap-5 lg:grid-cols-3">
|
<div className="grid gap-5 lg:grid-cols-3">
|
||||||
<div className="space-y-5 lg:col-span-2">
|
<div className="space-y-5 lg:col-span-2">
|
||||||
|
|
@ -941,6 +1025,16 @@ const TodoBoard = ({ listFormCode, searchParams, gridDto }: TodoBoardProps) => {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
<div>
|
||||||
|
<label className={labelClass}>
|
||||||
|
{translate('::App.Listform.ListformField.UserName')}
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
readOnly
|
||||||
|
className={`${inputClass} cursor-default`}
|
||||||
|
value={String(draft[userNameField] ?? '')}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
{options.statusExpr && (
|
{options.statusExpr && (
|
||||||
<div>
|
<div>
|
||||||
<label className={labelClass}>{translate('::ListForms.TodoBoard.Status')}</label>
|
<label className={labelClass}>{translate('::ListForms.TodoBoard.Status')}</label>
|
||||||
|
|
@ -957,7 +1051,7 @@ const TodoBoard = ({ listFormCode, searchParams, gridDto }: TodoBoardProps) => {
|
||||||
>
|
>
|
||||||
{statuses.map((status) => (
|
{statuses.map((status) => (
|
||||||
<option key={status} value={status}>
|
<option key={status} value={status}>
|
||||||
{status}
|
{lookupText(options.statusExpr, status)}
|
||||||
</option>
|
</option>
|
||||||
))}
|
))}
|
||||||
</select>
|
</select>
|
||||||
|
|
@ -1176,8 +1270,11 @@ const TodoBoard = ({ listFormCode, searchParams, gridDto }: TodoBoardProps) => {
|
||||||
</form>
|
</form>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<h4 className="min-w-0 truncate font-sm" title={status}>
|
<h4
|
||||||
{status}
|
className="min-w-0 truncate font-sm"
|
||||||
|
title={lookupText(options.statusExpr, status)}
|
||||||
|
>
|
||||||
|
{lookupText(options.statusExpr, status)}
|
||||||
</h4>
|
</h4>
|
||||||
{canUpdate && (
|
{canUpdate && (
|
||||||
<button
|
<button
|
||||||
|
|
@ -1412,7 +1509,7 @@ const TodoBoard = ({ listFormCode, searchParams, gridDto }: TodoBoardProps) => {
|
||||||
className="inline-flex items-center gap-1 rounded-full bg-blue-50 px-2 py-0.5 text-[11px] text-blue-700 dark:bg-blue-950 dark:text-blue-200"
|
className="inline-flex items-center gap-1 rounded-full bg-blue-50 px-2 py-0.5 text-[11px] text-blue-700 dark:bg-blue-950 dark:text-blue-200"
|
||||||
>
|
>
|
||||||
<FaTag />
|
<FaTag />
|
||||||
{tag}
|
{lookupText(options.tagExpr, tag)}
|
||||||
</span>
|
</span>
|
||||||
))}
|
))}
|
||||||
{tags.length > 3 && (
|
{tags.length > 3 && (
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue