mirror of
https://github.com/chartdb/chartdb.git
synced 2025-10-23 07:11:56 +00:00
feat(chart max length): add support for edit char max length (#613)
* feat(chart max length): add support for edit char max length * fix * update datatypes for max chars --------- Co-authored-by: johnnyfish <jonathanfishner11@gmail.com>
This commit is contained in:
@@ -151,6 +151,8 @@ export const ar: LanguageTranslation = {
|
||||
comments: 'تعليقات',
|
||||
no_comments: 'لا يوجد تعليقات',
|
||||
delete_field: 'حذف الحقل',
|
||||
// TODO: Translate
|
||||
character_length: 'Max Length',
|
||||
},
|
||||
index_actions: {
|
||||
title: 'خصائص الفهرس',
|
||||
|
@@ -152,6 +152,8 @@ export const bn: LanguageTranslation = {
|
||||
comments: 'মন্তব্য',
|
||||
no_comments: 'কোনো মন্তব্য নেই',
|
||||
delete_field: 'ফিল্ড মুছুন',
|
||||
// TODO: Translate
|
||||
character_length: 'Max Length',
|
||||
},
|
||||
index_actions: {
|
||||
title: 'ইনডেক্স কর্ম',
|
||||
|
@@ -153,6 +153,8 @@ export const de: LanguageTranslation = {
|
||||
comments: 'Kommentare',
|
||||
no_comments: 'Keine Kommentare',
|
||||
delete_field: 'Feld löschen',
|
||||
// TODO: Translate
|
||||
character_length: 'Max Length',
|
||||
},
|
||||
index_actions: {
|
||||
title: 'Indexattribute',
|
||||
|
@@ -145,6 +145,7 @@ export const en = {
|
||||
field_actions: {
|
||||
title: 'Field Attributes',
|
||||
unique: 'Unique',
|
||||
character_length: 'Max Length',
|
||||
comments: 'Comments',
|
||||
no_comments: 'No comments',
|
||||
delete_field: 'Delete Field',
|
||||
|
@@ -142,6 +142,8 @@ export const es: LanguageTranslation = {
|
||||
comments: 'Comentarios',
|
||||
no_comments: 'Sin comentarios',
|
||||
delete_field: 'Eliminar Campo',
|
||||
// TODO: Translate
|
||||
character_length: 'Max Length',
|
||||
},
|
||||
index_actions: {
|
||||
title: 'Atributos del Índice',
|
||||
|
@@ -140,6 +140,8 @@ export const fr: LanguageTranslation = {
|
||||
comments: 'Commentaires',
|
||||
no_comments: 'Pas de commentaires',
|
||||
delete_field: 'Supprimer le Champ',
|
||||
// TODO: Translate
|
||||
character_length: 'Max Length',
|
||||
},
|
||||
index_actions: {
|
||||
title: "Attributs de l'Index",
|
||||
|
@@ -153,6 +153,8 @@ export const gu: LanguageTranslation = {
|
||||
comments: 'ટિપ્પણીઓ',
|
||||
no_comments: 'કોઈ ટિપ્પણીઓ નથી',
|
||||
delete_field: 'ફીલ્ડ કાઢી નાખો',
|
||||
// TODO: Translate
|
||||
character_length: 'Max Length',
|
||||
},
|
||||
index_actions: {
|
||||
title: 'ઇન્ડેક્સ લક્ષણો',
|
||||
|
@@ -152,6 +152,8 @@ export const hi: LanguageTranslation = {
|
||||
comments: 'टिप्पणियाँ',
|
||||
no_comments: 'कोई टिप्पणी नहीं',
|
||||
delete_field: 'फ़ील्ड हटाएँ',
|
||||
// TODO: Translate
|
||||
character_length: 'Max Length',
|
||||
},
|
||||
index_actions: {
|
||||
title: 'सूचकांक विशेषताएँ',
|
||||
|
@@ -151,6 +151,8 @@ export const id_ID: LanguageTranslation = {
|
||||
comments: 'Komentar',
|
||||
no_comments: 'Tidak ada komentar',
|
||||
delete_field: 'Hapus Kolom',
|
||||
// TODO: Translate
|
||||
character_length: 'Max Length',
|
||||
},
|
||||
index_actions: {
|
||||
title: 'Atribut Indeks',
|
||||
|
@@ -155,6 +155,8 @@ export const ja: LanguageTranslation = {
|
||||
comments: 'コメント',
|
||||
no_comments: 'コメントがありません',
|
||||
delete_field: 'フィールドを削除',
|
||||
// TODO: Translate
|
||||
character_length: 'Max Length',
|
||||
},
|
||||
index_actions: {
|
||||
title: 'インデックス属性',
|
||||
|
@@ -151,6 +151,8 @@ export const ko_KR: LanguageTranslation = {
|
||||
comments: '주석',
|
||||
no_comments: '주석 없음',
|
||||
delete_field: '필드 삭제',
|
||||
// TODO: Translate
|
||||
character_length: 'Max Length',
|
||||
},
|
||||
index_actions: {
|
||||
title: '인덱스 속성',
|
||||
|
@@ -154,6 +154,8 @@ export const mr: LanguageTranslation = {
|
||||
comments: 'टिप्पण्या',
|
||||
no_comments: 'कोणत्याही टिप्पणी नाहीत',
|
||||
delete_field: 'फील्ड हटवा',
|
||||
// TODO: Translate
|
||||
character_length: 'Max Length',
|
||||
},
|
||||
index_actions: {
|
||||
title: 'इंडेक्स गुणधर्म',
|
||||
|
@@ -152,6 +152,8 @@ export const ne: LanguageTranslation = {
|
||||
comments: 'टिप्पणीहरू',
|
||||
no_comments: 'कुनै टिप्पणीहरू छैनन्',
|
||||
delete_field: 'क्षेत्र हटाउनुहोस्',
|
||||
// TODO: Translate
|
||||
character_length: 'Max Length',
|
||||
},
|
||||
index_actions: {
|
||||
title: 'सूचक विशेषताहरू',
|
||||
|
@@ -152,6 +152,8 @@ export const pt_BR: LanguageTranslation = {
|
||||
comments: 'Comentários',
|
||||
no_comments: 'Sem comentários',
|
||||
delete_field: 'Excluir Campo',
|
||||
// TODO: Translate
|
||||
character_length: 'Max Length',
|
||||
},
|
||||
index_actions: {
|
||||
title: 'Atributos do Índice',
|
||||
|
@@ -151,6 +151,8 @@ export const ru: LanguageTranslation = {
|
||||
comments: 'Комментарии',
|
||||
no_comments: 'Нет комментария',
|
||||
delete_field: 'Удалить поле',
|
||||
// TODO: Translate
|
||||
character_length: 'Max Length',
|
||||
},
|
||||
index_actions: {
|
||||
title: 'Атрибуты индекса',
|
||||
|
@@ -152,6 +152,8 @@ export const te: LanguageTranslation = {
|
||||
comments: 'వ్యాఖ్యలు',
|
||||
no_comments: 'వ్యాఖ్యలు లేవు',
|
||||
delete_field: 'ఫీల్డ్ తొలగించు',
|
||||
// TODO: Translate
|
||||
character_length: 'Max Length',
|
||||
},
|
||||
index_actions: {
|
||||
title: 'ఇండెక్స్ గుణాలు',
|
||||
|
@@ -151,6 +151,8 @@ export const tr: LanguageTranslation = {
|
||||
comments: 'Yorumlar',
|
||||
no_comments: 'Yorum yok',
|
||||
delete_field: 'Alanı Sil',
|
||||
// TODO: Translate
|
||||
character_length: 'Max Length',
|
||||
},
|
||||
index_actions: {
|
||||
title: 'İndeks Özellikleri',
|
||||
|
@@ -150,6 +150,8 @@ export const uk: LanguageTranslation = {
|
||||
comments: 'Коментарі',
|
||||
no_comments: 'Немає коментарів',
|
||||
delete_field: 'Видалити поле',
|
||||
// TODO: Translate
|
||||
character_length: 'Max Length',
|
||||
},
|
||||
index_actions: {
|
||||
title: 'Атрибути індексу',
|
||||
|
@@ -151,6 +151,8 @@ export const vi: LanguageTranslation = {
|
||||
comments: 'Bình luận',
|
||||
no_comments: 'Không có bình luận',
|
||||
delete_field: 'Xóa trường',
|
||||
// TODO: Translate
|
||||
character_length: 'Max Length',
|
||||
},
|
||||
index_actions: {
|
||||
title: 'Thuộc tính chỉ mục',
|
||||
|
@@ -148,6 +148,8 @@ export const zh_CN: LanguageTranslation = {
|
||||
comments: '注释',
|
||||
no_comments: '空',
|
||||
delete_field: '删除字段',
|
||||
// TODO: Translate
|
||||
character_length: 'Max Length',
|
||||
},
|
||||
index_actions: {
|
||||
title: '索引属性',
|
||||
|
@@ -148,6 +148,8 @@ export const zh_TW: LanguageTranslation = {
|
||||
comments: '註解',
|
||||
no_comments: '無註解',
|
||||
delete_field: '刪除欄位',
|
||||
// TODO: Translate
|
||||
character_length: 'Max Length',
|
||||
},
|
||||
index_actions: {
|
||||
title: '索引屬性',
|
||||
|
@@ -1,6 +1,6 @@
|
||||
import type { DataType } from './data-types';
|
||||
import type { DataTypeData } from './data-types';
|
||||
|
||||
export const clickhouseDataTypes: readonly DataType[] = [
|
||||
export const clickhouseDataTypes: readonly DataTypeData[] = [
|
||||
// Numeric Types
|
||||
{ name: 'uint8', id: 'uint8' },
|
||||
{ name: 'uint16', id: 'uint16' },
|
||||
@@ -48,25 +48,41 @@ export const clickhouseDataTypes: readonly DataType[] = [
|
||||
{ name: 'mediumblob', id: 'mediumblob' },
|
||||
{ name: 'tinyblob', id: 'tinyblob' },
|
||||
{ name: 'blob', id: 'blob' },
|
||||
{ name: 'varchar', id: 'varchar' },
|
||||
{ name: 'char', id: 'char' },
|
||||
{ name: 'varchar', id: 'varchar', hasCharMaxLength: true },
|
||||
{ name: 'char', id: 'char', hasCharMaxLength: true },
|
||||
{ name: 'char large object', id: 'char_large_object' },
|
||||
{ name: 'char varying', id: 'char_varying' },
|
||||
{ name: 'char varying', id: 'char_varying', hasCharMaxLength: true },
|
||||
{ name: 'character large object', id: 'character_large_object' },
|
||||
{ name: 'character varying', id: 'character_varying' },
|
||||
{
|
||||
name: 'character varying',
|
||||
id: 'character_varying',
|
||||
hasCharMaxLength: true,
|
||||
},
|
||||
{ name: 'nchar large object', id: 'nchar_large_object' },
|
||||
{ name: 'nchar varying', id: 'nchar_varying' },
|
||||
{ name: 'nchar varying', id: 'nchar_varying', hasCharMaxLength: true },
|
||||
{
|
||||
name: 'national character large object',
|
||||
id: 'national_character_large_object',
|
||||
},
|
||||
{ name: 'national character varying', id: 'national_character_varying' },
|
||||
{ name: 'national char varying', id: 'national_char_varying' },
|
||||
{ name: 'national character', id: 'national_character' },
|
||||
{ name: 'national char', id: 'national_char' },
|
||||
{
|
||||
name: 'national character varying',
|
||||
id: 'national_character_varying',
|
||||
hasCharMaxLength: true,
|
||||
},
|
||||
{
|
||||
name: 'national char varying',
|
||||
id: 'national_char_varying',
|
||||
hasCharMaxLength: true,
|
||||
},
|
||||
{
|
||||
name: 'national character',
|
||||
id: 'national_character',
|
||||
hasCharMaxLength: true,
|
||||
},
|
||||
{ name: 'national char', id: 'national_char', hasCharMaxLength: true },
|
||||
{ name: 'binary large object', id: 'binary_large_object' },
|
||||
{ name: 'binary varying', id: 'binary_varying' },
|
||||
{ name: 'fixedstring', id: 'fixedstring' },
|
||||
{ name: 'binary varying', id: 'binary_varying', hasCharMaxLength: true },
|
||||
{ name: 'fixedstring', id: 'fixedstring', hasCharMaxLength: true },
|
||||
{ name: 'string', id: 'string' },
|
||||
|
||||
// Date Types
|
||||
|
@@ -13,12 +13,16 @@ export interface DataType {
|
||||
name: string;
|
||||
}
|
||||
|
||||
export interface DataTypeData extends DataType {
|
||||
hasCharMaxLength?: boolean;
|
||||
}
|
||||
|
||||
export const dataTypeSchema: z.ZodType<DataType> = z.object({
|
||||
id: z.string(),
|
||||
name: z.string(),
|
||||
});
|
||||
|
||||
export const dataTypeMap: Record<DatabaseType, readonly DataType[]> = {
|
||||
export const dataTypeMap: Record<DatabaseType, readonly DataTypeData[]> = {
|
||||
[DatabaseType.GENERIC]: genericDataTypes,
|
||||
[DatabaseType.POSTGRESQL]: postgresDataTypes,
|
||||
[DatabaseType.MYSQL]: mysqlDataTypes,
|
||||
@@ -64,3 +68,21 @@ export function areFieldTypesCompatible(
|
||||
}
|
||||
|
||||
export const dataTypes = Object.values(dataTypeMap).flat();
|
||||
|
||||
export const dataTypeDataToDataType = (
|
||||
dataTypeData: DataTypeData
|
||||
): DataType => ({
|
||||
id: dataTypeData.id,
|
||||
name: dataTypeData.name,
|
||||
});
|
||||
|
||||
export const findDataTypeDataById = (
|
||||
id: string,
|
||||
databaseType?: DatabaseType
|
||||
): DataTypeData | undefined => {
|
||||
const dataTypesOptions = databaseType
|
||||
? dataTypeMap[databaseType]
|
||||
: dataTypes;
|
||||
|
||||
return dataTypesOptions.find((dataType) => dataType.id === id);
|
||||
};
|
||||
|
@@ -1,11 +1,11 @@
|
||||
import type { DataType } from './data-types';
|
||||
import type { DataTypeData } from './data-types';
|
||||
|
||||
export const genericDataTypes: readonly DataType[] = [
|
||||
export const genericDataTypes: readonly DataTypeData[] = [
|
||||
{ name: 'bigint', id: 'bigint' },
|
||||
{ name: 'binary', id: 'binary' },
|
||||
{ name: 'binary', id: 'binary', hasCharMaxLength: true },
|
||||
{ name: 'blob', id: 'blob' },
|
||||
{ name: 'boolean', id: 'boolean' },
|
||||
{ name: 'char', id: 'char' },
|
||||
{ name: 'char', id: 'char', hasCharMaxLength: true },
|
||||
{ name: 'date', id: 'date' },
|
||||
{ name: 'datetime', id: 'datetime' },
|
||||
{ name: 'decimal', id: 'decimal' },
|
||||
@@ -22,6 +22,6 @@ export const genericDataTypes: readonly DataType[] = [
|
||||
{ name: 'time', id: 'time' },
|
||||
{ name: 'timestamp', id: 'timestamp' },
|
||||
{ name: 'uuid', id: 'uuid' },
|
||||
{ name: 'varbinary', id: 'varbinary' },
|
||||
{ name: 'varchar', id: 'varchar' },
|
||||
{ name: 'varbinary', id: 'varbinary', hasCharMaxLength: true },
|
||||
{ name: 'varchar', id: 'varchar', hasCharMaxLength: true },
|
||||
] as const;
|
||||
|
@@ -1,6 +1,6 @@
|
||||
import type { DataType } from './data-types';
|
||||
import type { DataTypeData } from './data-types';
|
||||
|
||||
export const mariadbDataTypes: readonly DataType[] = [
|
||||
export const mariadbDataTypes: readonly DataTypeData[] = [
|
||||
// Numeric Types
|
||||
{ name: 'tinyint', id: 'tinyint' },
|
||||
{ name: 'smallint', id: 'smallint' },
|
||||
@@ -23,10 +23,10 @@ export const mariadbDataTypes: readonly DataType[] = [
|
||||
{ name: 'year', id: 'year' },
|
||||
|
||||
// String Types
|
||||
{ name: 'char', id: 'char' },
|
||||
{ name: 'varchar', id: 'varchar' },
|
||||
{ name: 'binary', id: 'binary' },
|
||||
{ name: 'varbinary', id: 'varbinary' },
|
||||
{ name: 'char', id: 'char', hasCharMaxLength: true },
|
||||
{ name: 'varchar', id: 'varchar', hasCharMaxLength: true },
|
||||
{ name: 'binary', id: 'binary', hasCharMaxLength: true },
|
||||
{ name: 'varbinary', id: 'varbinary', hasCharMaxLength: true },
|
||||
{ name: 'tinyblob', id: 'tinyblob' },
|
||||
{ name: 'blob', id: 'blob' },
|
||||
{ name: 'mediumblob', id: 'mediumblob' },
|
||||
|
@@ -1,6 +1,6 @@
|
||||
import type { DataType } from './data-types';
|
||||
import type { DataTypeData } from './data-types';
|
||||
|
||||
export const mysqlDataTypes: readonly DataType[] = [
|
||||
export const mysqlDataTypes: readonly DataTypeData[] = [
|
||||
// Numeric Types
|
||||
{ name: 'tinyint', id: 'tinyint' },
|
||||
{ name: 'smallint', id: 'smallint' },
|
||||
@@ -23,10 +23,10 @@ export const mysqlDataTypes: readonly DataType[] = [
|
||||
{ name: 'year', id: 'year' },
|
||||
|
||||
// String Types
|
||||
{ name: 'char', id: 'char' },
|
||||
{ name: 'varchar', id: 'varchar' },
|
||||
{ name: 'binary', id: 'binary' },
|
||||
{ name: 'varbinary', id: 'varbinary' },
|
||||
{ name: 'char', id: 'char', hasCharMaxLength: true },
|
||||
{ name: 'varchar', id: 'varchar', hasCharMaxLength: true },
|
||||
{ name: 'binary', id: 'binary', hasCharMaxLength: true },
|
||||
{ name: 'varbinary', id: 'varbinary', hasCharMaxLength: true },
|
||||
{ name: 'tinyblob', id: 'tinyblob' },
|
||||
{ name: 'blob', id: 'blob' },
|
||||
{ name: 'mediumblob', id: 'mediumblob' },
|
||||
|
@@ -1,6 +1,6 @@
|
||||
import type { DataType } from './data-types';
|
||||
import type { DataTypeData } from './data-types';
|
||||
|
||||
export const postgresDataTypes: readonly DataType[] = [
|
||||
export const postgresDataTypes: readonly DataTypeData[] = [
|
||||
// Numeric Types
|
||||
{ name: 'smallint', id: 'smallint' },
|
||||
{ name: 'integer', id: 'integer' },
|
||||
@@ -15,9 +15,13 @@ export const postgresDataTypes: readonly DataType[] = [
|
||||
{ name: 'money', id: 'money' },
|
||||
|
||||
// Character Types
|
||||
{ name: 'char', id: 'char' },
|
||||
{ name: 'varchar', id: 'varchar' },
|
||||
{ name: 'character varying', id: 'character_varying' },
|
||||
{ name: 'char', id: 'char', hasCharMaxLength: true },
|
||||
{ name: 'varchar', id: 'varchar', hasCharMaxLength: true },
|
||||
{
|
||||
name: 'character varying',
|
||||
id: 'character_varying',
|
||||
hasCharMaxLength: true,
|
||||
},
|
||||
{ name: 'text', id: 'text' },
|
||||
|
||||
// Binary Data Types
|
||||
|
@@ -1,6 +1,6 @@
|
||||
import type { DataType } from './data-types';
|
||||
import type { DataTypeData } from './data-types';
|
||||
|
||||
export const sqlServerDataTypes: readonly DataType[] = [
|
||||
export const sqlServerDataTypes: readonly DataTypeData[] = [
|
||||
// Exact Numerics
|
||||
{ name: 'bigint', id: 'bigint' },
|
||||
{ name: 'bit', id: 'bit' },
|
||||
@@ -25,18 +25,18 @@ export const sqlServerDataTypes: readonly DataType[] = [
|
||||
{ name: 'time', id: 'time' },
|
||||
|
||||
// Character Strings
|
||||
{ name: 'char', id: 'char' },
|
||||
{ name: 'varchar', id: 'varchar' },
|
||||
{ name: 'char', id: 'char', hasCharMaxLength: true },
|
||||
{ name: 'varchar', id: 'varchar', hasCharMaxLength: true },
|
||||
{ name: 'text', id: 'text' },
|
||||
|
||||
// Unicode Character Strings
|
||||
{ name: 'nchar', id: 'nchar' },
|
||||
{ name: 'nvarchar', id: 'nvarchar' },
|
||||
{ name: 'nchar', id: 'nchar', hasCharMaxLength: true },
|
||||
{ name: 'nvarchar', id: 'nvarchar', hasCharMaxLength: true },
|
||||
{ name: 'ntext', id: 'ntext' },
|
||||
|
||||
// Binary Strings
|
||||
{ name: 'binary', id: 'binary' },
|
||||
{ name: 'varbinary', id: 'varbinary' },
|
||||
{ name: 'binary', id: 'binary', hasCharMaxLength: true },
|
||||
{ name: 'varbinary', id: 'varbinary', hasCharMaxLength: true },
|
||||
{ name: 'image', id: 'image' },
|
||||
|
||||
// Other Data Types
|
||||
|
@@ -1,6 +1,6 @@
|
||||
import type { DataType } from './data-types';
|
||||
import type { DataTypeData } from './data-types';
|
||||
|
||||
export const sqliteDataTypes: readonly DataType[] = [
|
||||
export const sqliteDataTypes: readonly DataTypeData[] = [
|
||||
// Numeric Types
|
||||
{ name: 'integer', id: 'integer' },
|
||||
{ name: 'real', id: 'real' },
|
||||
@@ -22,6 +22,6 @@ export const sqliteDataTypes: readonly DataType[] = [
|
||||
{ name: 'int', id: 'int' },
|
||||
{ name: 'float', id: 'float' },
|
||||
{ name: 'boolean', id: 'boolean' },
|
||||
{ name: 'varchar', id: 'varchar' },
|
||||
{ name: 'varchar', id: 'varchar', hasCharMaxLength: true },
|
||||
{ name: 'decimal', id: 'decimal' },
|
||||
] as const;
|
||||
|
@@ -95,7 +95,7 @@ export const createFieldsFromMetadata = ({
|
||||
nullable: col.nullable,
|
||||
...(col.character_maximum_length &&
|
||||
col.character_maximum_length !== 'null'
|
||||
? { character_maximum_length: col.character_maximum_length }
|
||||
? { characterMaximumLength: col.character_maximum_length }
|
||||
: {}),
|
||||
...(col.precision?.precision
|
||||
? { precision: col.precision.precision }
|
||||
|
@@ -0,0 +1,156 @@
|
||||
import React, { useEffect, useRef } from 'react';
|
||||
import { Ellipsis, Trash2 } from 'lucide-react';
|
||||
import { Input } from '@/components/input/input';
|
||||
import { Button } from '@/components/button/button';
|
||||
import { Separator } from '@/components/separator/separator';
|
||||
import type { DBField } from '@/lib/domain/db-field';
|
||||
import { findDataTypeDataById } from '@/lib/data/data-types/data-types';
|
||||
import {
|
||||
Popover,
|
||||
PopoverContent,
|
||||
PopoverTrigger,
|
||||
} from '@/components/popover/popover';
|
||||
import { Label } from '@/components/label/label';
|
||||
import { Checkbox } from '@/components/checkbox/checkbox';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Textarea } from '@/components/textarea/textarea';
|
||||
import { debounce } from '@/lib/utils';
|
||||
|
||||
export interface TableFieldPopoverProps {
|
||||
field: DBField;
|
||||
updateField: (attrs: Partial<DBField>) => void;
|
||||
removeField: () => void;
|
||||
}
|
||||
|
||||
export const TableFieldPopover: React.FC<TableFieldPopoverProps> = ({
|
||||
field,
|
||||
updateField,
|
||||
removeField,
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const [localField, setLocalField] = React.useState<DBField>(field);
|
||||
|
||||
const debouncedUpdateFieldRef = useRef<((value?: DBField) => void) | null>(
|
||||
null
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
debouncedUpdateFieldRef.current = debounce((value?: DBField) => {
|
||||
updateField({
|
||||
comments: value?.comments,
|
||||
characterMaximumLength: value?.characterMaximumLength,
|
||||
unique: value?.unique,
|
||||
});
|
||||
}, 200);
|
||||
|
||||
return () => {
|
||||
debouncedUpdateFieldRef.current = null;
|
||||
};
|
||||
}, [updateField]);
|
||||
|
||||
useEffect(() => {
|
||||
if (debouncedUpdateFieldRef.current) {
|
||||
debouncedUpdateFieldRef.current(localField);
|
||||
}
|
||||
}, [localField]);
|
||||
|
||||
return (
|
||||
<Popover onOpenChange={(isOpen) => !isOpen && setLocalField(field)}>
|
||||
<PopoverTrigger asChild>
|
||||
<Button
|
||||
variant="ghost"
|
||||
className="h-8 w-[32px] p-2 text-slate-500 hover:bg-primary-foreground hover:text-slate-700 dark:text-slate-400 dark:hover:text-slate-200"
|
||||
>
|
||||
<Ellipsis className="size-3.5" />
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="w-52">
|
||||
<div className="flex flex-col gap-2">
|
||||
<div className="text-sm font-semibold">
|
||||
{t(
|
||||
'side_panel.tables_section.table.field_actions.title'
|
||||
)}
|
||||
</div>
|
||||
<Separator orientation="horizontal" />
|
||||
<div className="flex flex-col gap-3">
|
||||
<div className="flex items-center justify-between">
|
||||
<Label htmlFor="width" className="text-subtitle">
|
||||
{t(
|
||||
'side_panel.tables_section.table.field_actions.unique'
|
||||
)}
|
||||
</Label>
|
||||
<Checkbox
|
||||
checked={localField.unique}
|
||||
disabled={field.primaryKey}
|
||||
onCheckedChange={(value) =>
|
||||
setLocalField((current) => ({
|
||||
...current,
|
||||
unique: !!value,
|
||||
}))
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
{findDataTypeDataById(field.type.id)
|
||||
?.hasCharMaxLength ? (
|
||||
<div className="flex flex-col gap-2">
|
||||
<Label
|
||||
htmlFor="width"
|
||||
className="text-subtitle"
|
||||
>
|
||||
{t(
|
||||
'side_panel.tables_section.table.field_actions.character_length'
|
||||
)}
|
||||
</Label>
|
||||
<Input
|
||||
value={
|
||||
localField.characterMaximumLength ?? ''
|
||||
}
|
||||
type="number"
|
||||
onChange={(e) =>
|
||||
setLocalField((current) => ({
|
||||
...current,
|
||||
characterMaximumLength:
|
||||
e.target.value,
|
||||
}))
|
||||
}
|
||||
className="w-full rounded-md bg-muted text-sm"
|
||||
/>
|
||||
</div>
|
||||
) : null}
|
||||
<div className="flex flex-col gap-2">
|
||||
<Label htmlFor="width" className="text-subtitle">
|
||||
{t(
|
||||
'side_panel.tables_section.table.field_actions.comments'
|
||||
)}
|
||||
</Label>
|
||||
<Textarea
|
||||
value={localField.comments}
|
||||
onChange={(e) =>
|
||||
setLocalField((current) => ({
|
||||
...current,
|
||||
comments: e.target.value,
|
||||
}))
|
||||
}
|
||||
placeholder={t(
|
||||
'side_panel.tables_section.table.field_actions.no_comments'
|
||||
)}
|
||||
className="w-full rounded-md bg-muted text-sm"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<Separator orientation="horizontal" />
|
||||
<Button
|
||||
variant="outline"
|
||||
className="flex gap-2 !text-red-700"
|
||||
onClick={removeField}
|
||||
>
|
||||
<Trash2 className="size-3.5 text-red-700" />
|
||||
{t(
|
||||
'side_panel.tables_section.table.field_actions.delete_field'
|
||||
)}
|
||||
</Button>
|
||||
</div>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
);
|
||||
};
|
@@ -1,31 +1,23 @@
|
||||
import React, { useEffect, useRef } from 'react';
|
||||
import { Ellipsis, GripVertical, Trash2, KeyRound } from 'lucide-react';
|
||||
import React from 'react';
|
||||
import { GripVertical, KeyRound } from 'lucide-react';
|
||||
import { Input } from '@/components/input/input';
|
||||
import { Button } from '@/components/button/button';
|
||||
import { Separator } from '@/components/separator/separator';
|
||||
|
||||
import type { DBField } from '@/lib/domain/db-field';
|
||||
import { useChartDB } from '@/hooks/use-chartdb';
|
||||
import { dataTypeMap } from '@/lib/data/data-types/data-types';
|
||||
import {
|
||||
dataTypeDataToDataType,
|
||||
dataTypeMap,
|
||||
} from '@/lib/data/data-types/data-types';
|
||||
import {
|
||||
Tooltip,
|
||||
TooltipContent,
|
||||
TooltipTrigger,
|
||||
} from '@/components/tooltip/tooltip';
|
||||
import {
|
||||
Popover,
|
||||
PopoverContent,
|
||||
PopoverTrigger,
|
||||
} from '@/components/popover/popover';
|
||||
import { Label } from '@/components/label/label';
|
||||
import { Checkbox } from '@/components/checkbox/checkbox';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Textarea } from '@/components/textarea/textarea';
|
||||
import { TableFieldToggle } from './table-field-toggle';
|
||||
import { useSortable } from '@dnd-kit/sortable';
|
||||
import { CSS } from '@dnd-kit/utilities';
|
||||
import { SelectBox } from '@/components/select-box/select-box';
|
||||
import { debounce } from '@/lib/utils';
|
||||
import { TableFieldPopover } from './table-field-modal/table-field-modal';
|
||||
|
||||
export interface TableFieldProps {
|
||||
field: DBField;
|
||||
@@ -40,7 +32,7 @@ export const TableField: React.FC<TableFieldProps> = ({
|
||||
}) => {
|
||||
const { databaseType } = useChartDB();
|
||||
const { t } = useTranslation();
|
||||
const [comments, setComments] = React.useState(field.comments);
|
||||
|
||||
const { attributes, listeners, setNodeRef, transform, transition } =
|
||||
useSortable({ id: field.id });
|
||||
|
||||
@@ -54,28 +46,6 @@ export const TableField: React.FC<TableFieldProps> = ({
|
||||
transition,
|
||||
};
|
||||
|
||||
const debouncedUpdateCommentRef = useRef<((value?: string) => void) | null>(
|
||||
null
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
debouncedUpdateCommentRef.current = debounce((value?: string) => {
|
||||
updateField({
|
||||
comments: value,
|
||||
});
|
||||
}, 500);
|
||||
|
||||
return () => {
|
||||
debouncedUpdateCommentRef.current = null;
|
||||
};
|
||||
}, [updateField]);
|
||||
|
||||
useEffect(() => {
|
||||
if (debouncedUpdateCommentRef.current) {
|
||||
debouncedUpdateCommentRef.current(comments);
|
||||
}
|
||||
}, [comments]);
|
||||
|
||||
return (
|
||||
<div
|
||||
className="flex flex-1 touch-none flex-row justify-between p-1"
|
||||
@@ -122,8 +92,14 @@ export const TableField: React.FC<TableFieldProps> = ({
|
||||
value={field.type.id}
|
||||
onChange={(value) =>
|
||||
updateField({
|
||||
type: dataTypeMap[databaseType].find(
|
||||
(v) => v.id === value
|
||||
characterMaximumLength: undefined,
|
||||
type: dataTypeDataToDataType(
|
||||
dataTypeMap[databaseType].find(
|
||||
(v) => v.id === value
|
||||
) ?? {
|
||||
id: value as string,
|
||||
name: value as string,
|
||||
}
|
||||
),
|
||||
})
|
||||
}
|
||||
@@ -176,78 +152,11 @@ export const TableField: React.FC<TableFieldProps> = ({
|
||||
{t('side_panel.tables_section.table.primary_key')}
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
<Popover>
|
||||
<PopoverTrigger asChild>
|
||||
<Button
|
||||
variant="ghost"
|
||||
className="h-8 w-[32px] p-2 text-slate-500 hover:bg-primary-foreground hover:text-slate-700 dark:text-slate-400 dark:hover:text-slate-200"
|
||||
>
|
||||
<Ellipsis className="size-3.5" />
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="w-52">
|
||||
<div className="flex flex-col gap-2">
|
||||
<div className="text-sm font-semibold">
|
||||
{t(
|
||||
'side_panel.tables_section.table.field_actions.title'
|
||||
)}
|
||||
</div>
|
||||
<Separator orientation="horizontal" />
|
||||
<div className="flex flex-col gap-3">
|
||||
<div className="flex items-center justify-between">
|
||||
<Label
|
||||
htmlFor="width"
|
||||
className="text-subtitle"
|
||||
>
|
||||
{t(
|
||||
'side_panel.tables_section.table.field_actions.unique'
|
||||
)}
|
||||
</Label>
|
||||
<Checkbox
|
||||
checked={field.unique}
|
||||
disabled={field.primaryKey}
|
||||
onCheckedChange={(value) =>
|
||||
updateField({
|
||||
unique: !!value,
|
||||
})
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-col gap-2">
|
||||
<Label
|
||||
htmlFor="width"
|
||||
className="text-subtitle"
|
||||
>
|
||||
{t(
|
||||
'side_panel.tables_section.table.field_actions.comments'
|
||||
)}
|
||||
</Label>
|
||||
<Textarea
|
||||
value={comments}
|
||||
onChange={(e) =>
|
||||
setComments(e.target.value)
|
||||
}
|
||||
placeholder={t(
|
||||
'side_panel.tables_section.table.field_actions.no_comments'
|
||||
)}
|
||||
className="w-full rounded-md bg-muted text-sm"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<Separator orientation="horizontal" />
|
||||
<Button
|
||||
variant="outline"
|
||||
className="flex gap-2 !text-red-700"
|
||||
onClick={removeField}
|
||||
>
|
||||
<Trash2 className="size-3.5 text-red-700" />
|
||||
{t(
|
||||
'side_panel.tables_section.table.field_actions.delete_field'
|
||||
)}
|
||||
</Button>
|
||||
</div>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
<TableFieldPopover
|
||||
field={field}
|
||||
updateField={updateField}
|
||||
removeField={removeField}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
Reference in New Issue
Block a user