Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion packages/app-builder/src/components/Data/CreateField.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ function CreateFieldContent({ tableId, closeModal }: { tableId: string; closeMod
const selectedType = useStore(form.store, (state) => state.values.type);
const selectedEnum = useStore(form.store, (state) => state.values.isEnum);
const selectedUnique = useStore(form.store, (state) => state.values.isUnique);
const selectedRequired = useStore(form.store, (state) => state.values.required);

return (
<form
Expand Down Expand Up @@ -169,7 +170,7 @@ function CreateFieldContent({ tableId, closeModal }: { tableId: string; closeMod
>
{(field) => (
<div className="flex flex-1 flex-col gap-2">
<FormLabel name={field.name}>{t('data:create_field.required')}</FormLabel>
<FormLabel name={field.name}>{t('data:create_field.option_required')}</FormLabel>
<Select.Default
className="w-full overflow-hidden"
defaultValue={field.state.value}
Expand Down Expand Up @@ -222,6 +223,11 @@ function CreateFieldContent({ tableId, closeModal }: { tableId: string; closeMod
)}
</form.Field>
</div>
{selectedRequired === 'required' ? (
<span className="text-red-47 text-s">
{t('data:create_field.option_required_warning')}
</span>
) : null}
{EnumDataTypes.includes(selectedType) ? (
<form.Field
name="isEnum"
Expand Down
38 changes: 37 additions & 1 deletion packages/app-builder/src/components/Data/EditField.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,12 @@ import { getFieldErrors } from '@app-builder/utils/form';
import { useForm, useStore } from '@tanstack/react-form';
import { useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { Button, Checkbox, Modal } from 'ui-design-system';
import { Button, Checkbox, Modal, Select } from 'ui-design-system';

const REQUIRED_OPTIONS = [
{ value: 'optional', display: 'data:create_field.option_optional' },
{ value: 'required', display: 'data:create_field.option_required' },
] as const;

function disableEditUnique({
field,
Expand Down Expand Up @@ -79,6 +84,7 @@ export function EditField({
fieldId: inputField.id,
isEnum: inputField.isEnum,
isUnique: inputField.unicityConstraint !== 'no_unicity_constraint',
required: inputField.nullable ? 'optional' : 'required',
} as EditFieldPayload,
onSubmit: ({ value, formApi }) => {
if (formApi.state.isValid) {
Expand Down Expand Up @@ -139,6 +145,36 @@ export function EditField({
</div>
)}
</form.Field>
<form.Field
name="required"
validators={{
onChange: editFieldPayloadSchema.shape.required,
}}
>
{(field) => (
<div className="flex flex-col gap-2">
<FormLabel name={field.name}>
{t('data:create_field.option_required')}
</FormLabel>
<Select.Default
className="w-full overflow-hidden"
defaultValue={field.state.value}
onValueChange={(value) => {
field.handleChange(value as 'optional' | 'required');
}}
>
{REQUIRED_OPTIONS.map(({ value, display }) => {
return (
<Select.DefaultItem key={value} value={value}>
{t(display)}
</Select.DefaultItem>
);
})}
</Select.Default>
<FormErrorOrDescription errors={getFieldErrors(field.state.meta.errors)} />
</div>
)}
</form.Field>
</div>
{EnumDataTypes.includes(inputField.dataType) ? (
<form.Field name="isEnum">
Expand Down
33 changes: 20 additions & 13 deletions packages/app-builder/src/components/Data/TableDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,9 @@ function TableDetailFields({ fields, tableModel, dataModel }: TableDetailColumns
size: 100,
header: t('data:field_required'),
cell: ({ getValue }) => {
return getValue() ? t('data:nullable') : t('data:required');
return getValue()
? t('data:create_field.option_optional')
: t('data:create_field.option_required');
},
}),
fieldsColumnHelper.accessor('unicityConstraint', {
Expand Down Expand Up @@ -243,18 +245,22 @@ function TableDetailFields({ fields, tableModel, dataModel }: TableDetailColumns
size: 400,
cell: ({ cell }) => {
return (
<div className="flex flex-row items-center justify-between gap-1">
<FormatDescription description={cell.row.original.description} />
<div className="flex flex-row items-center gap-2">
<div className="min-w-0 flex-1">
<FormatDescription description={cell.row.original.description} />
</div>
{isEditDataModelFieldAvailable ? (
<EditField
key={cell.row.original.id}
field={cell.row.original}
linksToThisTable={linksToThisTable}
>
<div className="group-hover:text-grey-00 group-hover:bg-grey-98 group-hover:border-grey-50 hover:group-hover:bg-grey-95 active:group-hover:bg-grey-90 relative cursor-pointer rounded-sm border p-2 text-transparent transition-colors ease-in-out">
<Icon icon="edit-square" className="size-6" />
</div>
</EditField>
<div className="flex-shrink-0">
<EditField
key={cell.row.original.id}
field={cell.row.original}
linksToThisTable={linksToThisTable}
>
<div className="group-hover:text-grey-00 group-hover:bg-grey-98 group-hover:border-grey-50 hover:group-hover:bg-grey-95 active:group-hover:bg-grey-90 relative cursor-pointer rounded-sm border p-2 text-transparent transition-colors ease-in-out">
<Icon icon="edit-square" className="size-6" />
</div>
</EditField>
</div>
) : null}
</div>
);
Expand Down Expand Up @@ -354,9 +360,10 @@ function FormatDescription({ description }: { description: string }) {
return (
<span
className={clsx(
'relative first-letter:capitalize',
'relative first-letter:capitalize truncate block',
description ? 'text-grey-00' : 'text-grey-80',
)}
title={description || t('data:empty_description')}
>
{description || t('data:empty_description')}
</span>
Expand Down
4 changes: 1 addition & 3 deletions packages/app-builder/src/locales/ar/data.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"create_field.name_reserved_error": "الاسم \"id\" محجوز",
"create_field.option_optional": "اختياري",
"create_field.option_required": "متطلب",
"create_field.required": "المتطلب",
"create_field.option_required_warning": "يرجى الحذر عند إضافة حقل مطلوب على جدول فعال. إذا كنت تقوم بالفعل بإدخال البيانات أو إنشاء قرارات عبر الواجهة، سيفشل ذلك عند إضافة حقل مطلوب. نوصي بإنشاء الحقل أولاً كاختياري، إضافته إلى المصدر، ثم تحديده كمطلوب.",
"create_field.title": "انشاء حقل جديد",
"create_field.type": "النوع",
"create_field.type_bool": "منطقي",
Expand Down Expand Up @@ -74,10 +74,8 @@
"link_name": "الاسم",
"links_from_table": "روابط من إلى عناصر أخرى من<TableLocale>{{tableName}}</TableLocale>",
"menu.create_pivot.label": "تجميع القرارات حسب...",
"nullable": "يمكن أن يكون الحقل فارغ",
"parent_field_name": "اسم الحقل للجدول الرئيسي",
"parent_table": "جدول ذو صله",
"required": "متطلب",
"table_relationship": "العلاقة",
"unique.col_header": "فريد",
"upload_data": "رفع البيانات",
Expand Down
4 changes: 1 addition & 3 deletions packages/app-builder/src/locales/en/data.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"create_field.name_reserved_error": "The name \"id\" is reserved",
"create_field.option_optional": "Optional",
"create_field.option_required": "Required",
"create_field.required": "Required",
"create_field.option_required_warning": "Be careful when adding a required field on an active table. If you are already ingesting data or creating decisions via API, they will fail when you add a required field. We recommend you first create the field as optional, add it to your ingestion pipeline, and then mark it as required.",
"create_field.title": "Create a new field",
"create_field.type": "Type",
"create_field.type_bool": "Boolean",
Expand Down Expand Up @@ -74,10 +74,8 @@
"link_name": "Name",
"links_from_table": "Links from to other entities from <TableLocale>{{tableName}}</TableLocale>",
"menu.create_pivot.label": "Group decisions by...",
"nullable": "Nullable",
"parent_field_name": "Parent table field name",
"parent_table": "Related table",
"required": "Required",
"table_relationship": "Relationship",
"unique.col_header": "Unique",
"upload_data": "Upload data",
Expand Down
4 changes: 1 addition & 3 deletions packages/app-builder/src/locales/fr/data.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"create_field.name_reserved_error": "Le nom \"id\" est réservé",
"create_field.option_optional": "Facultatif",
"create_field.option_required": "Requis",
"create_field.required": "Requis",
"create_field.option_required_warning": "Faites attention lors de l'ajout d'un champ requis sur une table déjà utilisée. Si vous ingérez déjà des données ou créez des décisions par API, celles-ci échoueront si vous ajoutez un champ requis. Nous vous recommandons de créer d'abord le champ comme facultatif, de l'ajouter à votre pipeline d'ingestion, puise de le marquer comme requis.",
"create_field.title": "Créer un nouveau champ",
"create_field.type": "Type",
"create_field.type_bool": "Booléen",
Expand Down Expand Up @@ -74,10 +74,8 @@
"link_name": "Nom",
"links_from_table": "Liens vers d'autres entités de <TableLocale>{{tableName}}</TableLocale>",
"menu.create_pivot.label": "Regrouper les décisions par...",
"nullable": "Nullable",
"parent_field_name": "Nom du champ de la table parent",
"parent_table": "Table associée",
"required": "Requis",
"table_relationship": "Relation",
"unique.col_header": "Unique",
"upload_data": "Téléverser des données",
Expand Down
2 changes: 2 additions & 0 deletions packages/app-builder/src/models/data-model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -247,13 +247,15 @@ export interface UpdateFieldInput {
description?: string;
isEnum?: boolean;
isUnique?: boolean;
isNullable?: boolean;
}

export function adaptUpdateFieldDto(updateFieldInput: UpdateFieldInput): UpdateTableFieldDto {
return {
description: updateFieldInput.description,
is_enum: updateFieldInput.isEnum,
is_unique: updateFieldInput.isUnique,
is_nullable: updateFieldInput.isNullable,
};
}

Expand Down
1 change: 1 addition & 0 deletions packages/app-builder/src/queries/data/edit-field.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export const editFieldPayloadSchema = z.object({
fieldId: z.uuid(),
isEnum: z.boolean(),
isUnique: z.boolean(),
required: z.enum(['optional', 'required']),
});

export type EditFieldPayload = z.infer<typeof editFieldPayloadSchema>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,14 @@ export async function action({ request }: ActionFunctionArgs) {
const { success, error, data } = editFieldPayloadSchema.safeParse(raw);

if (!success) return json({ success: 'false', errors: z.treeifyError(error) });
const { description, fieldId, isEnum, isUnique } = data;
const { description, fieldId, isEnum, isUnique, required } = data;

try {
await dataModelRepository.patchDataModelField(fieldId, {
description,
isEnum,
isUnique,
isNullable: required === 'optional',
});

return json({ success: 'true', errors: [] });
Expand Down
2 changes: 2 additions & 0 deletions packages/marble-api/openapis/marblecore-api/data-model.yml
Original file line number Diff line number Diff line change
Expand Up @@ -575,6 +575,8 @@ components:
type: boolean
is_unique:
type: boolean
is_nullable:
type: boolean
CreateTableLinkBody:
type: object
required:
Expand Down
1 change: 1 addition & 0 deletions packages/marble-api/src/generated/marblecore-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1020,6 +1020,7 @@ export type UpdateTableFieldDto = {
description?: string;
is_enum?: boolean;
is_unique?: boolean;
is_nullable?: boolean;
};
export type CreateTableLinkBody = {
name: string;
Expand Down