mirror of
https://github.com/AppFlowy-IO/AppFlowy-Web.git
synced 2025-11-30 19:37:55 +08:00
Merge branch 'main' into chore/fix-publish-relation
This commit is contained in:
@@ -107,9 +107,7 @@ export function getCellDataText(cell: YDatabaseCell, field: YDatabaseField, curr
|
||||
return (
|
||||
data
|
||||
.split(',')
|
||||
.map((item) => {
|
||||
return options?.find((option) => option.id === item)?.name;
|
||||
})
|
||||
.map((item) => options?.find((option) => option?.id === item)?.name)
|
||||
.filter((item) => item)
|
||||
.join(',') || ''
|
||||
);
|
||||
|
||||
@@ -187,13 +187,19 @@ function generateGroupByField(field: YDatabaseField) {
|
||||
case FieldType.MultiSelect: {
|
||||
group.set(YjsDatabaseKey.content, '');
|
||||
const typeOption = parseSelectOptionTypeOptions(field);
|
||||
const options = typeOption?.options || [];
|
||||
const options = (typeOption?.options || []).filter((option) => Boolean(option && option.id));
|
||||
|
||||
columns.push([{ id: fieldId, visible: true }]);
|
||||
|
||||
// Add a column for each option
|
||||
options.forEach((option) => {
|
||||
columns.push([{ id: option.id, visible: true }]);
|
||||
const optionId = option?.id;
|
||||
|
||||
if (!optionId) {
|
||||
return;
|
||||
}
|
||||
|
||||
columns.push([{ id: optionId, visible: true }]);
|
||||
});
|
||||
break;
|
||||
}
|
||||
@@ -2678,13 +2684,20 @@ export function useSwitchPropertyType() {
|
||||
// 1. to RichText
|
||||
if ([FieldType.RichText, FieldType.URL].includes(fieldType)) {
|
||||
const cellType = Number(cell.get(YjsDatabaseKey.field_type));
|
||||
const typeOption = field.get(YjsDatabaseKey.type_option)?.get(String(cellType));
|
||||
const existingTypeOption = field
|
||||
.get(YjsDatabaseKey.type_option)
|
||||
?.get(String(cellType)) as YMapFieldTypeOption | undefined;
|
||||
|
||||
switch (cellType) {
|
||||
// From Number to RichText, keep the number format value
|
||||
case FieldType.Number: {
|
||||
const formatRaw = existingTypeOption?.get(YjsDatabaseKey.format);
|
||||
const parsedFormat =
|
||||
formatRaw === undefined || formatRaw === null ? undefined : Number(formatRaw);
|
||||
const format =
|
||||
(Number(typeOption.get(YjsDatabaseKey.format)) as NumberFormat) ?? NumberFormat.Num;
|
||||
parsedFormat === undefined || Number.isNaN(parsedFormat)
|
||||
? NumberFormat.Num
|
||||
: (parsedFormat as NumberFormat);
|
||||
|
||||
if (data) {
|
||||
newData = EnhancedBigStats.parse(data.toString(), format) || '';
|
||||
@@ -2696,8 +2709,13 @@ export function useSwitchPropertyType() {
|
||||
case FieldType.SingleSelect:
|
||||
case FieldType.MultiSelect: {
|
||||
const selectedIds = (data as string).split(',');
|
||||
const typeOption = typeOptionMap.get(String(cellType));
|
||||
const content = typeOption.get(YjsDatabaseKey.content);
|
||||
const optionSource = typeOptionMap?.get(String(cellType)) as YMapFieldTypeOption | undefined;
|
||||
const content = optionSource?.get(YjsDatabaseKey.content);
|
||||
|
||||
if (typeof content !== 'string') {
|
||||
newData = '';
|
||||
break;
|
||||
}
|
||||
|
||||
try {
|
||||
const parsedContent = JSON.parse(content) as SelectTypeOption;
|
||||
@@ -2766,9 +2784,12 @@ export function useSwitchPropertyType() {
|
||||
|
||||
// 3. to SingleSelect or MultiSelect
|
||||
if ([FieldType.SingleSelect, FieldType.MultiSelect].includes(fieldType)) {
|
||||
const typeOption = typeOptionMap.get(String(fieldType));
|
||||
const content = typeOption.get(YjsDatabaseKey.content);
|
||||
const targetTypeOption = typeOptionMap?.get(String(fieldType)) as
|
||||
| YMapFieldTypeOption
|
||||
| undefined;
|
||||
const content = targetTypeOption?.get(YjsDatabaseKey.content);
|
||||
|
||||
if (typeof content === 'string') {
|
||||
try {
|
||||
const parsedContent = JSON.parse(content) as SelectTypeOption;
|
||||
const options = parsedContent.options;
|
||||
@@ -2790,6 +2811,9 @@ export function useSwitchPropertyType() {
|
||||
} catch (e) {
|
||||
// do nothing
|
||||
}
|
||||
} else {
|
||||
newData = '';
|
||||
}
|
||||
}
|
||||
|
||||
// 4. to DateTime
|
||||
@@ -2996,12 +3020,14 @@ export function useAddSelectOption(fieldId: string) {
|
||||
|
||||
if (group) {
|
||||
const columns = group.get(YjsDatabaseKey.groups);
|
||||
const column = columns.toArray().find((col) => col.id === option.id);
|
||||
const optionId = option.id;
|
||||
|
||||
const column = columns.toArray().find((col) => col.id === optionId);
|
||||
|
||||
if (!column) {
|
||||
columns.push([
|
||||
{
|
||||
id: option.id,
|
||||
id: optionId,
|
||||
visible: true,
|
||||
},
|
||||
]);
|
||||
|
||||
@@ -7,6 +7,10 @@ export interface ChecklistCellData {
|
||||
percentage: number;
|
||||
}
|
||||
|
||||
function normalizeChecklistOptions(options: SelectOption[] = []) {
|
||||
return options.filter((option): option is SelectOption => Boolean(option && option.id));
|
||||
}
|
||||
|
||||
export function parseChecklistData(data: string): ChecklistCellData | null {
|
||||
try {
|
||||
const { options, selected_option_ids } = JSON.parse(data);
|
||||
@@ -39,13 +43,14 @@ export function addTask(data: string, taskName: string): string {
|
||||
}
|
||||
|
||||
const { options = [], selectedOptionIds } = parsedData;
|
||||
const normalizedOptions = normalizeChecklistOptions(options);
|
||||
|
||||
if (options.find((option) => option.id === task.id)) {
|
||||
if (normalizedOptions.find((option) => option.id === task.id)) {
|
||||
return data;
|
||||
}
|
||||
|
||||
return JSON.stringify({
|
||||
options: [...options, task],
|
||||
options: [...normalizedOptions, task],
|
||||
selected_option_ids: selectedOptionIds,
|
||||
});
|
||||
}
|
||||
@@ -58,6 +63,7 @@ export function toggleSelectedTask(data: string, taskId: string): string {
|
||||
}
|
||||
|
||||
const { options, selectedOptionIds = [] } = parsedData;
|
||||
const normalizedOptions = normalizeChecklistOptions(options);
|
||||
|
||||
const isSelected = selectedOptionIds.includes(taskId);
|
||||
const newSelectedOptionIds = isSelected
|
||||
@@ -65,7 +71,7 @@ export function toggleSelectedTask(data: string, taskId: string): string {
|
||||
: [...selectedOptionIds, taskId];
|
||||
|
||||
return JSON.stringify({
|
||||
options,
|
||||
options: normalizedOptions,
|
||||
selected_option_ids: newSelectedOptionIds,
|
||||
});
|
||||
}
|
||||
@@ -78,8 +84,9 @@ export function updateTask(data: string, taskId: string, taskName: string): stri
|
||||
}
|
||||
|
||||
const { options = [], selectedOptionIds } = parsedData;
|
||||
const normalizedOptions = normalizeChecklistOptions(options);
|
||||
|
||||
const newOptions = options.map((option) => {
|
||||
const newOptions = normalizedOptions.map((option) => {
|
||||
if (option.id === taskId) {
|
||||
return {
|
||||
...option,
|
||||
@@ -104,8 +111,9 @@ export function removeTask(data: string, taskId: string): string {
|
||||
}
|
||||
|
||||
const { options = [], selectedOptionIds = [] } = parsedData;
|
||||
const normalizedOptions = normalizeChecklistOptions(options);
|
||||
|
||||
const newOptions = options.filter((option) => option.id !== taskId);
|
||||
const newOptions = normalizedOptions.filter((option) => option.id !== taskId);
|
||||
const newSelectedOptionIds = selectedOptionIds.filter((id) => id !== taskId);
|
||||
|
||||
return JSON.stringify({
|
||||
@@ -122,15 +130,16 @@ export function reorderTasks(data: string, { beforeId, taskId }: { beforeId?: st
|
||||
}
|
||||
|
||||
const { selectedOptionIds, options = [] } = parsedData;
|
||||
const normalizedOptions = normalizeChecklistOptions(options);
|
||||
|
||||
const index = options.findIndex((opt) => opt.id === taskId);
|
||||
const option = options[index];
|
||||
const index = normalizedOptions.findIndex((opt) => opt.id === taskId);
|
||||
const option = normalizedOptions[index];
|
||||
|
||||
if (index === -1) {
|
||||
return data;
|
||||
}
|
||||
|
||||
const newOptions = [...options];
|
||||
const newOptions = [...normalizedOptions];
|
||||
const beforeIndex = newOptions.findIndex((opt) => opt.id === beforeId);
|
||||
|
||||
if (beforeIndex === index) {
|
||||
|
||||
@@ -27,7 +27,7 @@ export function parseSelectOptionCellData(field: YDatabaseField, data: string) {
|
||||
|
||||
return selectedIds
|
||||
.map((id) => {
|
||||
const option = typeOption?.options?.find((option) => option.id === id);
|
||||
const option = typeOption?.options?.find((option) => option?.id === id);
|
||||
|
||||
return option?.name ?? '';
|
||||
})
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { YDatabaseField, YjsDatabaseKey } from '@/application/types';
|
||||
import { YDatabaseField, YMapFieldTypeOption, YjsDatabaseKey } from '@/application/types';
|
||||
import { FieldType } from '@/application/database-yjs';
|
||||
|
||||
export function getTypeOptions (field: YDatabaseField) {
|
||||
export function getTypeOptions(field?: YDatabaseField): YMapFieldTypeOption | undefined {
|
||||
const fieldType = Number(field?.get(YjsDatabaseKey.type)) as FieldType;
|
||||
|
||||
return field?.get(YjsDatabaseKey.type_option)?.get(String(fieldType));
|
||||
|
||||
@@ -41,9 +41,11 @@ export function getGroupColumns(field: YDatabaseField) {
|
||||
return [{ id: field.get(YjsDatabaseKey.id) }];
|
||||
}
|
||||
|
||||
const options = typeOption.options.map((option) => ({
|
||||
id: option.id,
|
||||
}));
|
||||
const options = typeOption.options
|
||||
.map((option) => ({
|
||||
id: option?.id,
|
||||
}))
|
||||
.filter((option): option is { id: string } => Boolean(option.id));
|
||||
|
||||
return [{ id: field.get(YjsDatabaseKey.id) }, ...options];
|
||||
}
|
||||
@@ -114,7 +116,11 @@ export function groupBySelectOption(
|
||||
}
|
||||
|
||||
typeOption.options.forEach((option) => {
|
||||
const groupName = option.id;
|
||||
const groupName = option?.id;
|
||||
|
||||
if (!groupName) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (filter) {
|
||||
const condition = Number(filter?.get(YjsDatabaseKey.condition)) as SelectOptionFilterCondition;
|
||||
@@ -156,7 +162,7 @@ export function groupBySelectOption(
|
||||
}
|
||||
|
||||
selectedIds.forEach((id) => {
|
||||
const option = typeOption.options.find((option) => option.id === id);
|
||||
const option = typeOption.options.find((option) => option?.id === id);
|
||||
const groupName = option?.id ?? fieldId;
|
||||
|
||||
if (!result.has(groupName)) {
|
||||
|
||||
@@ -1294,7 +1294,11 @@ export const useSelectFieldOptions = (fieldId: string, searchValue?: string) =>
|
||||
|
||||
if (!typeOption) return [] as SelectOption[];
|
||||
|
||||
return typeOption.options.filter((option) => {
|
||||
const normalizedOptions = typeOption.options.filter((option): option is SelectOption => {
|
||||
return Boolean(option && option.id && option.name);
|
||||
});
|
||||
|
||||
return normalizedOptions.filter((option) => {
|
||||
if (!searchValue) return true;
|
||||
return option.name.toLowerCase().includes(searchValue.toLowerCase());
|
||||
});
|
||||
|
||||
@@ -30,7 +30,7 @@ function ColumnRename({
|
||||
const option = useMemo(() => {
|
||||
if (!field || ![FieldType.SingleSelect, FieldType.MultiSelect].includes(fieldType)) return;
|
||||
|
||||
return parseSelectOptionTypeOptions(field)?.options.find((option) => option.id === id);
|
||||
return parseSelectOptionTypeOptions(field)?.options.find((option) => option?.id === id);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [field, clock, id]);
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ export function useRenderColumn(id: string, fieldId: string) {
|
||||
</div>
|
||||
);
|
||||
if ([FieldType.SingleSelect, FieldType.MultiSelect].includes(fieldType)) {
|
||||
const option = parseSelectOptionTypeOptions(field)?.options.find((option) => option.id === id);
|
||||
const option = parseSelectOptionTypeOptions(field)?.options.find((option) => option?.id === id);
|
||||
const isFieldId = fieldId === id;
|
||||
const label = isFieldId ? `${t('button.no')} ${fieldName}` : option?.name || '';
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ export function SelectOptionCell({
|
||||
const renderSelectedOptions = useCallback(
|
||||
(selected: string[]) =>
|
||||
selected.map((id) => {
|
||||
const option = typeOption?.options?.find((option) => option.id === id);
|
||||
const option = typeOption?.options?.find((option) => option?.id === id);
|
||||
|
||||
if (!option) return null;
|
||||
return (
|
||||
|
||||
@@ -79,7 +79,7 @@ function SelectOptionCellMenu ({ open, onOpenChange, fieldId, rowId, selectOptio
|
||||
if (!typeOption) return [];
|
||||
|
||||
return selectOptionIds.map((id) => {
|
||||
const option = typeOption.options?.find((option) => option.id === id);
|
||||
const option = typeOption.options?.find((option) => option?.id === id);
|
||||
|
||||
if (!option) return null;
|
||||
return {
|
||||
@@ -151,6 +151,7 @@ function SelectOptionCellMenu ({ open, onOpenChange, fieldId, rowId, selectOptio
|
||||
const lastOption = options[options.length - 1];
|
||||
|
||||
if (hoveredId === 'create') {
|
||||
if (!lastOption) return;
|
||||
setHoveredId(lastOption.id);
|
||||
return;
|
||||
}
|
||||
@@ -167,7 +168,11 @@ function SelectOptionCellMenu ({ open, onOpenChange, fieldId, rowId, selectOptio
|
||||
return;
|
||||
}
|
||||
|
||||
const nextHoveredId = options[hoveredIndex - 1].id;
|
||||
const previousOption = options[hoveredIndex - 1];
|
||||
|
||||
if (!previousOption) return;
|
||||
|
||||
const nextHoveredId = previousOption.id;
|
||||
|
||||
setHoveredId(nextHoveredId);
|
||||
|
||||
@@ -181,6 +186,7 @@ function SelectOptionCellMenu ({ open, onOpenChange, fieldId, rowId, selectOptio
|
||||
const firstOption = options[0];
|
||||
|
||||
if (hoveredId === 'create') {
|
||||
if (!firstOption) return;
|
||||
setHoveredId(firstOption.id);
|
||||
return;
|
||||
}
|
||||
@@ -197,7 +203,11 @@ function SelectOptionCellMenu ({ open, onOpenChange, fieldId, rowId, selectOptio
|
||||
return;
|
||||
}
|
||||
|
||||
const nextHoveredId = options[hoveredIndex + 1].id;
|
||||
const nextOption = options[hoveredIndex + 1];
|
||||
|
||||
if (!nextOption) return;
|
||||
|
||||
const nextHoveredId = nextOption.id;
|
||||
|
||||
setHoveredId(nextHoveredId);
|
||||
}, [createdShow, options]);
|
||||
|
||||
@@ -48,5 +48,9 @@ export function SelectOptionList({
|
||||
);
|
||||
|
||||
if (!field || !typeOption) return null;
|
||||
return <div className={'flex flex-col'}>{typeOption.options.map(renderOption)}</div>;
|
||||
const normalizedOptions = typeOption.options.filter((option): option is SelectOption => {
|
||||
return Boolean(option && option.id && option.name);
|
||||
});
|
||||
|
||||
return <div className={'flex flex-col'}>{normalizedOptions.map(renderOption)}</div>;
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ function SelectFilterContentOverview({ filter, field }: { filter: SelectOptionFi
|
||||
|
||||
const options = filter.optionIds
|
||||
.map((optionId) => {
|
||||
const option = typeOption?.options?.find((option) => option.id === optionId);
|
||||
const option = typeOption?.options?.find((option) => option?.id === optionId);
|
||||
|
||||
return option?.name;
|
||||
})
|
||||
|
||||
@@ -22,7 +22,8 @@ function DataTimePropertyMenuContent({
|
||||
const { t } = useTranslation();
|
||||
|
||||
const typeOption = useFieldTypeOption(fieldId);
|
||||
const includeTime = Boolean(typeOption.get(YjsDatabaseKey.include_time));
|
||||
const includeTimeRaw = typeOption?.get(YjsDatabaseKey.include_time);
|
||||
const includeTime = typeof includeTimeRaw === 'boolean' ? includeTimeRaw : Boolean(includeTimeRaw);
|
||||
|
||||
const updateFormat = useUpdateDateTimeFieldFormat(fieldId);
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { getFieldDateTimeFormats } from '@/application/database-yjs';
|
||||
import { useUpdateDateTimeFieldFormat } from '@/application/database-yjs/dispatch';
|
||||
import { DateFormat, TimeFormat, YjsDatabaseKey } from '@/application/types';
|
||||
import { DateFormat, TimeFormat } from '@/application/types';
|
||||
import { useFieldTypeOption } from '@/components/database/components/cell/Cell.hooks';
|
||||
import {
|
||||
DropdownMenuGroup, DropdownMenuItem, DropdownMenuItemTick,
|
||||
@@ -17,11 +18,7 @@ function DateTimeFormatGroup ({
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
const typeOption = useFieldTypeOption(fieldId);
|
||||
const typeOptionDateFormat = typeOption.get(YjsDatabaseKey.date_format);
|
||||
const typeOptionTimeFormat = typeOption.get(YjsDatabaseKey.time_format);
|
||||
|
||||
const selectedDateFormat = typeOptionDateFormat !== undefined ? Number(typeOptionDateFormat) : undefined;
|
||||
const selectedTimeFormat = typeOptionTimeFormat !== undefined ? Number(typeOptionTimeFormat) : undefined;
|
||||
const { dateFormat: selectedDateFormat, timeFormat: selectedTimeFormat } = getFieldDateTimeFormats(typeOption, undefined);
|
||||
|
||||
const updateFormat = useUpdateDateTimeFieldFormat(fieldId);
|
||||
const dateFormats = useMemo(() => [{
|
||||
|
||||
Reference in New Issue
Block a user