make non-fillable fields visible but disabled in UI

This commit is contained in:
dswbx
2025-10-24 14:07:37 +02:00
parent 292e4595ea
commit f2aad9caac
11 changed files with 353 additions and 37 deletions

View File

@@ -136,8 +136,10 @@ export class Entity<
.map((field) => (alias ? `${alias}.${field.name} as ${field.name}` : field.name));
}
getFillableFields(context?: TActionContext, include_virtual?: boolean): Field[] {
return this.getFields(include_virtual).filter((field) => field.isFillable(context));
getFillableFields(context?: "create" | "update", include_virtual?: boolean): Field[] {
return this.getFields({ virtual: include_virtual }).filter((field) =>
field.isFillable(context),
);
}
getRequiredFields(): Field[] {
@@ -189,9 +191,15 @@ export class Entity<
return this.fields.findIndex((field) => field.name === name) !== -1;
}
getFields(include_virtual: boolean = false): Field[] {
if (include_virtual) return this.fields;
return this.fields.filter((f) => !f.isVirtual());
getFields({
virtual = false,
primary = true,
}: { virtual?: boolean; primary?: boolean } = {}): Field[] {
return this.fields.filter((f) => {
if (!virtual && f.isVirtual()) return false;
if (!primary && f instanceof PrimaryField) return false;
return true;
});
}
addField(field: Field) {
@@ -231,7 +239,7 @@ export class Entity<
}
}
const fields = this.getFillableFields(context, false);
const fields = this.getFillableFields(context as any, false);
if (options?.ignoreUnknown !== true) {
const field_names = fields.map((f) => f.name);
@@ -275,7 +283,7 @@ export class Entity<
fields = this.getFillableFields(options.context);
break;
default:
fields = this.getFields(true);
fields = this.getFields({ virtual: true });
}
const _fields = Object.fromEntries(fields.map((field) => [field.name, field]));

View File

@@ -83,8 +83,10 @@ export class Mutator<
}
// we should never get here, but just to be sure (why?)
if (!field.isFillable(context)) {
throw new Error(`Field "${key}" is not fillable on entity "${entity.name}"`);
if (!field.isFillable(context as any)) {
throw new Error(
`Field "${key}" of entity "${entity.name}" is not fillable on context "${context}"`,
);
}
// transform from field

View File

@@ -26,11 +26,19 @@ export const baseFieldConfigSchema = s
.strictObject({
label: s.string(),
description: s.string(),
required: s.boolean({ default: false }),
fillable: s.anyOf([
s.boolean({ title: "Boolean" }),
s.array(s.string({ enum: ActionContext }), { title: "Context", uniqueItems: true }),
]),
required: s.boolean({ default: DEFAULT_REQUIRED }),
fillable: s.anyOf(
[
s.boolean({ title: "Boolean" }),
s.array(s.string({ enum: ["create", "update"] }), {
title: "Context",
uniqueItems: true,
}),
],
{
default: DEFAULT_FILLABLE,
},
),
hidden: s.anyOf([
s.boolean({ title: "Boolean" }),
// @todo: tmp workaround
@@ -103,7 +111,7 @@ export abstract class Field<
return this.config?.default_value;
}
isFillable(context?: TActionContext): boolean {
isFillable(context?: "create" | "update"): boolean {
if (Array.isArray(this.config.fillable)) {
return context ? this.config.fillable.includes(context) : DEFAULT_FILLABLE;
}
@@ -165,7 +173,7 @@ export abstract class Field<
// @todo: add field level validation
isValid(value: any, context: TActionContext): boolean {
if (typeof value !== "undefined") {
return this.isFillable(context);
return this.isFillable(context as any);
} else if (context === "create") {
return !this.isRequired();
}

View File

@@ -99,6 +99,7 @@ export function fieldTestSuite(
const _config = {
..._requiredConfig,
required: false,
fillable: true,
};
function fieldJson(field: Field) {
@@ -116,10 +117,7 @@ export function fieldTestSuite(
expect(fieldJson(fillable)).toEqual({
type: noConfigField.type,
config: {
..._config,
fillable: true,
},
config: _config,
});
expect(fieldJson(required)).toEqual({
@@ -150,7 +148,6 @@ export function fieldTestSuite(
type: requiredAndDefault.type,
config: {
..._config,
fillable: true,
required: true,
default_value: config.defaultValue,
},

View File

@@ -77,7 +77,7 @@ export class SchemaManager {
}
getIntrospectionFromEntity(entity: Entity): IntrospectedTable {
const fields = entity.getFields(false);
const fields = entity.getFields({ virtual: false });
const indices = this.em.getIndicesOf(entity);
// this is intentionally setting values to defaults, like "nullable" and "default"