mirror of
https://github.com/AppFlowy-IO/AppFlowy-Web.git
synced 2026-03-13 10:00:26 +08:00
feat: display person property (#86)
* feat: person property * feat: person property part 2 * feat: person property part 3 filter menu * Update AppContextConsumer.tsx * chore: one line * chore: disable person from properties menu * chore: disable when tab is inactive
This commit is contained in:
@@ -510,7 +510,8 @@
|
||||
"relationField": "Relate to another database. Useful for linking items across databases",
|
||||
"AISummaryField": "Summarize the content of the record. Useful for getting a quick overview",
|
||||
"AITranslateField": "Translate the content of the record. Useful for multilingual support",
|
||||
"mediaField": "Add images, videos, or other media. Useful for rich content"
|
||||
"mediaField": "Add images, videos, or other media. Useful for rich content",
|
||||
"personField": "Select a person from your workspace. Useful for assigning tasks or ownership"
|
||||
},
|
||||
"sideBar": {
|
||||
"closeSidebar": "Close sidebar",
|
||||
@@ -1653,6 +1654,12 @@
|
||||
"isEmpty": "Is empty",
|
||||
"isNotEmpty": "Is not empty"
|
||||
},
|
||||
"personFilter": {
|
||||
"contains": "Contains",
|
||||
"doesNotContain": "Does not contain",
|
||||
"isEmpty": "Is empty",
|
||||
"isNotEmpty": "Is not empty"
|
||||
},
|
||||
"field": {
|
||||
"label": "Property",
|
||||
"hide": "Hide property",
|
||||
@@ -1679,6 +1686,7 @@
|
||||
"timeFieldName": "Time",
|
||||
"mediaFieldName": "Files & media",
|
||||
"translateFieldName": "AI Translate",
|
||||
"personFieldName": "Person",
|
||||
"translateTo": "Translate to",
|
||||
"numberFormat": "Number format",
|
||||
"dateFormat": "Date format",
|
||||
|
||||
@@ -103,6 +103,11 @@ export interface RelationCell extends Cell {
|
||||
|
||||
export type RelationCellData = RowId[];
|
||||
|
||||
export interface PersonCell extends Cell {
|
||||
fieldType: FieldType.Person;
|
||||
data: string;
|
||||
}
|
||||
|
||||
export interface CellProps<T extends Cell> {
|
||||
cell?: T;
|
||||
rowId: string;
|
||||
|
||||
@@ -20,7 +20,8 @@ export enum FieldType {
|
||||
Relation = 10,
|
||||
AISummaries = 11,
|
||||
AITranslations = 12,
|
||||
FileMedia = 14
|
||||
FileMedia = 14,
|
||||
Person = 15,
|
||||
}
|
||||
|
||||
export enum CalculationType {
|
||||
@@ -104,4 +105,4 @@ export enum DateGroupCondition {
|
||||
Week = 2,
|
||||
Month = 3,
|
||||
Year = 4
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ export * from './text';
|
||||
export * from './checkbox';
|
||||
export * from './checklist';
|
||||
export * from './relation';
|
||||
export * from './person';
|
||||
|
||||
export function getFieldName (fieldType: FieldType) {
|
||||
switch (fieldType) {
|
||||
@@ -39,6 +40,8 @@ export function getFieldName (fieldType: FieldType) {
|
||||
return 'AI Summary';
|
||||
case FieldType.AITranslations:
|
||||
return 'AI Translate';
|
||||
case FieldType.Person:
|
||||
return 'Person';
|
||||
default:
|
||||
return 'Text';
|
||||
}
|
||||
|
||||
2
src/application/database-yjs/fields/person/index.ts
Normal file
2
src/application/database-yjs/fields/person/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export * from './person.type';
|
||||
export * from './parse';
|
||||
36
src/application/database-yjs/fields/person/parse.ts
Normal file
36
src/application/database-yjs/fields/person/parse.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
import { YDatabaseField, YjsDatabaseKey } from "@/application/types";
|
||||
|
||||
import { getTypeOptions } from "../type_option";
|
||||
|
||||
import { PersonCellData, PersonTypeOption } from "./person.type";
|
||||
|
||||
export function parsePersonTypeOptions(field: YDatabaseField) {
|
||||
const content = getTypeOptions(field)?.get(YjsDatabaseKey.content);
|
||||
|
||||
if (!content)
|
||||
return {
|
||||
persons: [],
|
||||
};
|
||||
|
||||
try {
|
||||
return JSON.parse(content) as PersonTypeOption;
|
||||
} catch (e) {
|
||||
return {
|
||||
persons: [],
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export function parsePersonCellData(field: YDatabaseField, data: string): PersonCellData | null {
|
||||
const users = parsePersonTypeOptions(field)?.persons;
|
||||
|
||||
if (!data) return null;
|
||||
|
||||
try {
|
||||
const userIds = JSON.parse(data);
|
||||
|
||||
return { users: users.filter((user) => userIds.includes(user.id)) };
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
29
src/application/database-yjs/fields/person/person.type.ts
Normal file
29
src/application/database-yjs/fields/person/person.type.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import { Filter } from '@/application/database-yjs';
|
||||
|
||||
export enum PersonFilterCondition {
|
||||
PersonContains = 0,
|
||||
PersonDoesNotContain = 1,
|
||||
PersonIsEmpty = 2,
|
||||
PersonIsNotEmpty = 3,
|
||||
}
|
||||
|
||||
export interface PersonFilter extends Filter {
|
||||
condition: PersonFilterCondition;
|
||||
userIds: string[];
|
||||
}
|
||||
|
||||
export interface PersonTypeOption {
|
||||
persons: {
|
||||
id: string,
|
||||
name?: string,
|
||||
avatar_url?: string,
|
||||
}[];
|
||||
}
|
||||
|
||||
export interface PersonCellData {
|
||||
users: {
|
||||
id: string,
|
||||
name?: string,
|
||||
avatar_url?: string,
|
||||
}[];
|
||||
}
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
NumberFilter,
|
||||
NumberFilterCondition,
|
||||
parseChecklistData,
|
||||
PersonFilterCondition,
|
||||
SelectOptionFilter,
|
||||
SelectOptionFilterCondition,
|
||||
TextFilter,
|
||||
@@ -87,6 +88,22 @@ export function parseFilter(fieldType: FieldType, filter: YDatabaseFilter) {
|
||||
condition: DateFilterCondition.DateStartsOn,
|
||||
};
|
||||
}
|
||||
|
||||
case FieldType.Person:
|
||||
try {
|
||||
const personIds = JSON.parse(value.content) as string[];
|
||||
|
||||
return {
|
||||
...value,
|
||||
personIds,
|
||||
};
|
||||
} catch (e) {
|
||||
console.error('Error parsing person filter content:', e);
|
||||
return {
|
||||
...value,
|
||||
personIds: [],
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
@@ -158,6 +175,10 @@ export function filterBy(
|
||||
return rowTimeFilterCheck(data, filterValue as DateFilter);
|
||||
}
|
||||
|
||||
case FieldType.Person: {
|
||||
return personFilterCheck(cellData, content, condition);
|
||||
}
|
||||
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
@@ -359,6 +380,42 @@ export function selectOptionFilterCheck(data: string, content: string, condition
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export function personFilterCheck(data: string, content: string, condition: number) {
|
||||
let userIds: string[] = [];
|
||||
let filterIds: string[] = [];
|
||||
|
||||
try {
|
||||
userIds = JSON.parse(data || '[]');
|
||||
filterIds = JSON.parse(content || '[]');
|
||||
} catch (e) {
|
||||
console.error('Error parsing person filter data:', e);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (PersonFilterCondition.PersonIsEmpty === condition) {
|
||||
return filterIds.length === 0 || data === '';
|
||||
}
|
||||
|
||||
if (PersonFilterCondition.PersonIsNotEmpty === condition) {
|
||||
return filterIds.length > 0 && data !== '';
|
||||
}
|
||||
|
||||
switch (condition) {
|
||||
case PersonFilterCondition.PersonContains:
|
||||
if (filterIds.length === 0) return true;
|
||||
return some(filterIds, (id) => userIds.includes(id));
|
||||
|
||||
case PersonFilterCondition.PersonDoesNotContain:
|
||||
if (filterIds.length === 0) return true;
|
||||
return every(filterIds, (id) => !userIds.includes(id));
|
||||
|
||||
// Default case, if no conditions match
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Return the default value for the filter
|
||||
export function textFilterFillData(content: string, condition: number) {
|
||||
switch (condition) {
|
||||
@@ -566,6 +623,21 @@ export function dateFilterFillData(filter: YDatabaseFilter): {
|
||||
}
|
||||
}
|
||||
|
||||
export function personFilterFillData(content: string, condition: number) {
|
||||
switch (condition) {
|
||||
case PersonFilterCondition.PersonContains:
|
||||
return content;
|
||||
case PersonFilterCondition.PersonDoesNotContain:
|
||||
return '';
|
||||
case PersonFilterCondition.PersonIsEmpty:
|
||||
return '';
|
||||
case PersonFilterCondition.PersonIsNotEmpty:
|
||||
return content;
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
export function filterFillData(filter: YDatabaseFilter, field: YDatabaseField) {
|
||||
const content = filter.get(YjsDatabaseKey.content);
|
||||
const condition = Number(filter.get(YjsDatabaseKey.condition));
|
||||
@@ -585,6 +657,8 @@ export function filterFillData(filter: YDatabaseFilter, field: YDatabaseField) {
|
||||
return selectOptionFilterFillData(content, condition);
|
||||
case FieldType.Checklist:
|
||||
return checklistFilterFillData(content, condition);
|
||||
case FieldType.Person:
|
||||
return personFilterFillData(content, condition);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
@@ -630,5 +704,10 @@ export function getDefaultFilterCondition(fieldType: FieldType) {
|
||||
timestamp: dayjs().startOf('day').unix(),
|
||||
}),
|
||||
};
|
||||
case FieldType.Person:
|
||||
return {
|
||||
condition: PersonFilterCondition.PersonContains,
|
||||
content: '',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import React, { memo, Suspense } from 'react';
|
||||
import React, { memo, Suspense, useEffect, useRef, useState } from 'react';
|
||||
import { useSearchParams } from 'react-router-dom';
|
||||
import { Awareness } from 'y-protocols/awareness';
|
||||
|
||||
import { AIChatProvider } from '@/components/ai-chat/AIChatProvider';
|
||||
import { AppOverlayProvider } from '@/components/app/app-overlay/AppOverlayProvider';
|
||||
import { AppContext } from '@/components/app/app.hooks';
|
||||
import { AppContext, useAppViewId, useCurrentWorkspaceId } from '@/components/app/app.hooks';
|
||||
import RequestAccess from '@/components/app/landing-pages/RequestAccess';
|
||||
import { useCurrentUser } from '@/components/main/app.hooks';
|
||||
|
||||
import { useAllContextData } from '../hooks/useAllContextData';
|
||||
|
||||
@@ -41,9 +43,53 @@ export const AppContextConsumer: React.FC<AppContextConsumerProps> = memo(
|
||||
/>
|
||||
</Suspense>
|
||||
}
|
||||
{<OpenClient />}
|
||||
</AppOverlayProvider>
|
||||
</AIChatProvider>
|
||||
</AppContext.Provider>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
function OpenClient() {
|
||||
const currentWorkspaceId = useCurrentWorkspaceId();
|
||||
const viewId = useAppViewId();
|
||||
const [searchParams] = useSearchParams();
|
||||
const openClient = searchParams.get('is_desktop') === 'true';
|
||||
const rowId = searchParams.get('r');
|
||||
const currentUser = useCurrentUser();
|
||||
|
||||
const [isTabVisible, setIsTabVisible] = useState(true);
|
||||
const prevOpenClientRef = useRef(openClient);
|
||||
|
||||
useEffect(() => {
|
||||
const handleVisibilityChange = () => {
|
||||
setIsTabVisible(document.visibilityState === 'visible');
|
||||
};
|
||||
|
||||
document.addEventListener('visibilitychange', handleVisibilityChange);
|
||||
|
||||
setIsTabVisible(document.visibilityState === 'visible');
|
||||
|
||||
return () => {
|
||||
document.removeEventListener('visibilitychange', handleVisibilityChange);
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (isTabVisible && openClient && currentUser) {
|
||||
if (!prevOpenClientRef.current) {
|
||||
window.open(
|
||||
`appflowy-flutter://open-page?workspace_id=${currentWorkspaceId}&view_id=${viewId}&email=${currentUser.email}${
|
||||
rowId ? `&row_id=${rowId}` : ''
|
||||
}`,
|
||||
'_self'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
prevOpenClientRef.current = openClient;
|
||||
}, [currentWorkspaceId, viewId, currentUser, openClient, rowId, isTabVisible]);
|
||||
|
||||
return <></>;
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ import { SelectOptionCell } from '@/components/database/components/cell/select-o
|
||||
import { TextCell } from '@/components/database/components/cell/text';
|
||||
|
||||
import { FileMediaCell } from 'src/components/database/components/cell/file-media';
|
||||
import { PersonCell } from './person';
|
||||
|
||||
export function Cell(props: CellProps<CellType>) {
|
||||
const { rowId, fieldId, style, wrap, isHovering } = props;
|
||||
@@ -44,6 +45,8 @@ export function Cell(props: CellProps<CellType>) {
|
||||
case FieldType.AISummaries:
|
||||
case FieldType.AITranslations:
|
||||
return AITextCell;
|
||||
case FieldType.Person:
|
||||
return PersonCell;
|
||||
default:
|
||||
return TextCell;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
import { useMemo } from 'react';
|
||||
|
||||
import { parsePersonCellData, useFieldSelector } from '@/application/database-yjs';
|
||||
import { CellProps, PersonCell as PersonCellType } from '@/application/database-yjs/cell.type';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar';
|
||||
|
||||
export function PersonCell({ cell, style, placeholder, fieldId, wrap }: CellProps<PersonCellType>) {
|
||||
const { field, clock } = useFieldSelector(fieldId);
|
||||
|
||||
const users = useMemo(() => {
|
||||
return parsePersonCellData(field, cell?.data ?? '');
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [cell?.data, field, clock]);
|
||||
|
||||
const isEmpty = !users || !users.users.length;
|
||||
|
||||
const renderedUsers = useMemo(() => {
|
||||
if (!users) return;
|
||||
|
||||
return users?.users.map((user) => (
|
||||
<div key={user.id} className='min-w-fit max-w-[120px]'>
|
||||
<div className='flex items-center gap-1'>
|
||||
<Avatar className='h-5 w-5 border border-border-primary'>
|
||||
<AvatarImage src={user.avatar_url} alt={''} />
|
||||
<AvatarFallback>
|
||||
{user.avatar_url ? <span className='flex justify-center text-sm'>{user.avatar_url}</span> : user.name}
|
||||
</AvatarFallback>
|
||||
</Avatar>
|
||||
<span className='truncate text-sm'>{user.name}</span>
|
||||
</div>
|
||||
</div>
|
||||
));
|
||||
}, [users]);
|
||||
|
||||
return (
|
||||
<div
|
||||
style={style}
|
||||
className={cn(
|
||||
'select-option-cell flex w-full items-center gap-1',
|
||||
isEmpty && placeholder ? 'text-text-tertiary' : '',
|
||||
wrap
|
||||
? 'flex-wrap overflow-x-hidden'
|
||||
: 'appflowy-hidden-scroller h-full w-full flex-nowrap overflow-x-auto overflow-y-hidden'
|
||||
)}
|
||||
>
|
||||
{isEmpty ? placeholder || null : renderedUsers}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
1
src/components/database/components/cell/person/index.ts
Normal file
1
src/components/database/components/cell/person/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from './PersonCell';
|
||||
@@ -36,9 +36,13 @@ function PropertiesMenu({
|
||||
}
|
||||
|
||||
if (
|
||||
[FieldType.Relation, FieldType.AISummaries, FieldType.AITranslations, FieldType.FileMedia].includes(
|
||||
property.type
|
||||
)
|
||||
[
|
||||
FieldType.Relation,
|
||||
FieldType.AISummaries,
|
||||
FieldType.AITranslations,
|
||||
FieldType.FileMedia,
|
||||
FieldType.Person,
|
||||
].includes(property.type)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ function FieldLabel ({ type, ...props }: { type: FieldType } & React.HTMLAttribu
|
||||
[FieldType.AISummaries]: t('grid.field.summaryFieldName'),
|
||||
[FieldType.AITranslations]: t('grid.field.translateFieldName'),
|
||||
[FieldType.FileMedia]: t('grid.field.mediaFieldName'),
|
||||
[FieldType.Person]: t('grid.field.personFieldName'),
|
||||
}[type];
|
||||
}, [t, type]);
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ import { ReactComponent as RelationSvg } from '@/assets/icons/relation.svg';
|
||||
import { ReactComponent as AISummariesSvg } from '@/assets/icons/ai_summary.svg';
|
||||
import { ReactComponent as AITranslationsSvg } from '@/assets/icons/ai_translate.svg';
|
||||
import { ReactComponent as FileMediaSvg } from '@/assets/icons/attachment.svg';
|
||||
import { ReactComponent as PersonSvg } from '@/assets/icons/user.svg';
|
||||
|
||||
export const FieldTypeSvgMap: Record<FieldType, FC<React.SVGProps<SVGSVGElement>>> = {
|
||||
[FieldType.RichText]: TextSvg,
|
||||
@@ -30,6 +31,7 @@ export const FieldTypeSvgMap: Record<FieldType, FC<React.SVGProps<SVGSVGElement>
|
||||
[FieldType.AISummaries]: AISummariesSvg,
|
||||
[FieldType.AITranslations]: AITranslationsSvg,
|
||||
[FieldType.FileMedia]: FileMediaSvg,
|
||||
[FieldType.Person]: PersonSvg,
|
||||
};
|
||||
|
||||
export const FieldTypeIcon: FC<{ type: FieldType; className?: string }> = memo(({ type, ...props }) => {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { DateFilter, FieldType, Filter, SelectOptionFilter, useFieldSelector } from '@/application/database-yjs';
|
||||
import { DateFilter, FieldType, Filter, PersonFilter, SelectOptionFilter, useFieldSelector } from '@/application/database-yjs';
|
||||
import { YjsDatabaseKey } from '@/application/types';
|
||||
import DateTimeFilterMenu from '@/components/database/components/filters/filter-menu/DateTimeFilterMenu';
|
||||
import { useMemo } from 'react';
|
||||
@@ -6,10 +6,11 @@ import CheckboxFilterMenu from './CheckboxFilterMenu';
|
||||
import ChecklistFilterMenu from './ChecklistFilterMenu';
|
||||
import MultiSelectOptionFilterMenu from './MultiSelectOptionFilterMenu';
|
||||
import NumberFilterMenu from './NumberFilterMenu';
|
||||
import PersonFilterMenu from './PersonFilterMenu';
|
||||
import SingleSelectOptionFilterMenu from './SingleSelectOptionFilterMenu';
|
||||
import TextFilterMenu from './TextFilterMenu';
|
||||
|
||||
export function FilterMenu ({ filter }: { filter: Filter }) {
|
||||
export function FilterMenu({ filter }: { filter: Filter }) {
|
||||
const { field } = useFieldSelector(filter?.fieldId);
|
||||
const fieldType = Number(field?.get(YjsDatabaseKey.type)) as FieldType;
|
||||
|
||||
@@ -33,6 +34,8 @@ export function FilterMenu ({ filter }: { filter: Filter }) {
|
||||
case FieldType.LastEditedTime:
|
||||
case FieldType.CreatedTime:
|
||||
return <DateTimeFilterMenu filter={filter as DateFilter} />;
|
||||
case FieldType.Person:
|
||||
return <PersonFilterMenu filter={filter as PersonFilter} />;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
import { PersonFilter } from '@/application/database-yjs';
|
||||
import FieldMenuTitle from '@/components/database/components/filters/filter-menu/FieldMenuTitle';
|
||||
|
||||
function PersonFilterMenu({ filter }: { filter: PersonFilter }) {
|
||||
return (
|
||||
<div className={'flex flex-col'}>
|
||||
<FieldMenuTitle fieldId={filter.fieldId} filterId={filter.id} renderConditionSelect={<></>} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default PersonFilterMenu;
|
||||
@@ -3,6 +3,7 @@ import {
|
||||
ChecklistFilterCondition,
|
||||
FieldType,
|
||||
Filter,
|
||||
PersonFilterCondition,
|
||||
SelectOptionFilter,
|
||||
useFieldSelector,
|
||||
} from '@/application/database-yjs';
|
||||
@@ -52,6 +53,21 @@ export function FilterContentOverview({ filter }: { filter: Filter }) {
|
||||
: t('grid.checklistFilter.isIncomplted')}
|
||||
</>
|
||||
);
|
||||
case FieldType.Person:
|
||||
return (
|
||||
<>
|
||||
:{' '}
|
||||
{filter.condition === PersonFilterCondition.PersonContains
|
||||
? t('grid.personFilter.contains')
|
||||
: filter.condition === PersonFilterCondition.PersonDoesNotContain
|
||||
? t('grid.personFilter.doesNotContain')
|
||||
: filter.condition === PersonFilterCondition.PersonIsEmpty
|
||||
? t('grid.personFilter.isEmpty')
|
||||
: filter.condition === PersonFilterCondition.PersonIsNotEmpty
|
||||
? t('grid.personFilter.isNotEmpty')
|
||||
: ''}
|
||||
</>
|
||||
);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ import { TextProperty } from '@/components/database/components/property/text';
|
||||
import { FC, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { ChecklistProperty } from 'src/components/database/components/property/cheklist';
|
||||
import { PersonCell } from '../cell/person';
|
||||
|
||||
export function Property ({ fieldId, rowId }: { fieldId: string; rowId: string }) {
|
||||
const cell = useCellSelector({
|
||||
@@ -48,6 +49,8 @@ export function Property ({ fieldId, rowId }: { fieldId: string; rowId: string }
|
||||
return TextCell;
|
||||
case FieldType.FileMedia:
|
||||
return FileMediaCell;
|
||||
case FieldType.Person:
|
||||
return PersonCell;
|
||||
default:
|
||||
return TextProperty;
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ const properties = [
|
||||
FieldType.Relation,
|
||||
FieldType.AISummaries,
|
||||
FieldType.AITranslations,
|
||||
FieldType.Person,
|
||||
];
|
||||
|
||||
export function PropertySelectTrigger({ fieldId, disabled }: { fieldId: string; disabled?: boolean }) {
|
||||
@@ -62,6 +63,7 @@ export function PropertySelectTrigger({ fieldId, disabled }: { fieldId: string;
|
||||
[FieldType.AISummaries]: t('tooltip.AISummaryField'),
|
||||
[FieldType.AITranslations]: t('tooltip.AITranslateField'),
|
||||
[FieldType.FileMedia]: t('tooltip.mediaField'),
|
||||
[FieldType.Person]: t('tooltip.personField'),
|
||||
};
|
||||
}, [t]);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user