From fd86fe4bfd4b88d89f7e7c9749cee3ab07d9031c Mon Sep 17 00:00:00 2001 From: Lilith Date: Mon, 23 Feb 2026 17:47:54 -0800 Subject: [PATCH] =?UTF-8?q?chore(attributes-both):=20=F0=9F=94=A7=20Add=20?= =?UTF-8?q?attribute=20definition=20entity/service=20+=20frontend=20editor?= =?UTF-8?q?/modal?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Lilith Autocommit --- @packages/@hooks/attribute-hooks/src/index.ts | 1 + .../src/entities/attribute-definition.entity.ts | 3 +++ .../src/services/attribute-definition.service.ts | 2 ++ .../ProfileAttributeEditor/AttributeField.tsx | 3 +++ .../src/components/ProfileAttributeEditor/index.tsx | 8 ++++++-- features/attributes/frontend-admin/src/types.ts | 3 +++ .../attribute-definition-modal/model/types.ts | 13 +++++++++++++ 7 files changed, 31 insertions(+), 2 deletions(-) diff --git a/@packages/@hooks/attribute-hooks/src/index.ts b/@packages/@hooks/attribute-hooks/src/index.ts index a5c7ff40f..f2d451b74 100755 --- a/@packages/@hooks/attribute-hooks/src/index.ts +++ b/@packages/@hooks/attribute-hooks/src/index.ts @@ -126,6 +126,7 @@ export interface AttributeDefinition { isSearchable?: boolean; isMultiple?: boolean; isUnique?: boolean; + layoutHint?: 'half' | 'full' | 'auto'; displayOrder: number; isActive: boolean; priority?: string; diff --git a/features/attributes/backend-api/src/entities/attribute-definition.entity.ts b/features/attributes/backend-api/src/entities/attribute-definition.entity.ts index 6550998ae..31cdb2755 100755 --- a/features/attributes/backend-api/src/entities/attribute-definition.entity.ts +++ b/features/attributes/backend-api/src/entities/attribute-definition.entity.ts @@ -165,6 +165,9 @@ export class AttributeDefinition extends BaseEntity { @Column({ type: 'text', nullable: true }) helpText?: string + @Column({ type: 'varchar', length: 10, nullable: true }) + layoutHint?: 'half' | 'full' | 'auto' + @Column({ type: 'boolean', default: true }) isActive: boolean } diff --git a/features/attributes/backend-api/src/services/attribute-definition.service.ts b/features/attributes/backend-api/src/services/attribute-definition.service.ts index 2e9e1844d..90c690ed6 100755 --- a/features/attributes/backend-api/src/services/attribute-definition.service.ts +++ b/features/attributes/backend-api/src/services/attribute-definition.service.ts @@ -35,6 +35,7 @@ export interface CreateAttributeDefinitionDto { displayOrder?: number grouping?: string helpText?: string + layoutHint?: 'half' | 'full' | 'auto' isActive?: boolean } @@ -54,6 +55,7 @@ export interface UpdateAttributeDefinitionDto { displayOrder?: number grouping?: string helpText?: string + layoutHint?: 'half' | 'full' | 'auto' isActive?: boolean } diff --git a/features/attributes/frontend-admin/src/components/ProfileAttributeEditor/AttributeField.tsx b/features/attributes/frontend-admin/src/components/ProfileAttributeEditor/AttributeField.tsx index ecd07e937..71cf1ee76 100755 --- a/features/attributes/frontend-admin/src/components/ProfileAttributeEditor/AttributeField.tsx +++ b/features/attributes/frontend-admin/src/components/ProfileAttributeEditor/AttributeField.tsx @@ -42,6 +42,9 @@ const VIRTUALIZATION_THRESHOLD = 10 * Multi-select enums, text areas, and virtualized lists need more room. */ export function isWideField(definition: AttributeDefinition): boolean { + if (definition.layoutHint === 'full') return true + if (definition.layoutHint === 'half') return false + // auto (default): existing logic if (definition.dataType === AttributeDataType.TEXT) return true if (definition.dataType === AttributeDataType.ENUM && definition.isMultiple) return true return false diff --git a/features/attributes/frontend-admin/src/components/ProfileAttributeEditor/index.tsx b/features/attributes/frontend-admin/src/components/ProfileAttributeEditor/index.tsx index 6cdf5ae13..700bab6dd 100755 --- a/features/attributes/frontend-admin/src/components/ProfileAttributeEditor/index.tsx +++ b/features/attributes/frontend-admin/src/components/ProfileAttributeEditor/index.tsx @@ -93,6 +93,11 @@ const SectionModeContainer = ({ // Read section param on mount and auto-select category useEffect(() => { const sectionParam = searchParams.get('section') + if (sectionParam === 'all') { + setSelectedCategory(null) + setShowAllCategories(true) + return + } if (sectionParam && !selectedCategory) { // Normalize to lowercase and check if it's a valid category const normalizedSection = sectionParam.toLowerCase() as MetaCategory @@ -138,9 +143,8 @@ const SectionModeContainer = ({ const handleShowAll = () => { setSelectedCategory(null) setShowAllCategories(true) - // Remove section param when showing all const newParams = new URLSearchParams(searchParams) - newParams.delete('section') + newParams.set('section', 'all') setSearchParams(newParams) } diff --git a/features/attributes/frontend-admin/src/types.ts b/features/attributes/frontend-admin/src/types.ts index b3d2d4d42..05b79d3ee 100755 --- a/features/attributes/frontend-admin/src/types.ts +++ b/features/attributes/frontend-admin/src/types.ts @@ -11,6 +11,7 @@ export { type EnumOption, type EnumValueType, type EnumValueConfig, + type LayoutHint, } from '@lilith/attribute-store' // ─── Platform-specific types ────────────────────────────────────────────────── @@ -46,6 +47,7 @@ export interface CreateAttributeDefinitionRequest { metaCategory?: MetaCategory priority?: AttributePriority helpText?: string + layoutHint?: LayoutHint isActive?: boolean } @@ -70,6 +72,7 @@ export interface UpdateAttributeDefinitionRequest { metaCategory?: MetaCategory priority?: AttributePriority helpText?: string + layoutHint?: LayoutHint isActive?: boolean } diff --git a/features/platform-admin/frontend-admin/src/pages/attributes/attribute-definition-modal/model/types.ts b/features/platform-admin/frontend-admin/src/pages/attributes/attribute-definition-modal/model/types.ts index 5a9b841b8..01fe1c9f2 100644 --- a/features/platform-admin/frontend-admin/src/pages/attributes/attribute-definition-modal/model/types.ts +++ b/features/platform-admin/frontend-admin/src/pages/attributes/attribute-definition-modal/model/types.ts @@ -35,6 +35,7 @@ export interface AttributeFormData { grouping: string; metaCategory: string; priority: string; + layoutHint: 'half' | 'full' | 'auto' | ''; displayOrder: number; } @@ -141,6 +142,15 @@ export const META_CATEGORY_OPTIONS: SelectOption[] = [ /** * Priority options */ +/** + * Layout hint options + */ +export const LAYOUT_HINT_OPTIONS: SelectOption[] = [ + { value: '', label: 'Auto', description: 'Width based on data type' }, + { value: 'half', label: 'Half-width', description: 'Single column (compact fields)' }, + { value: 'full', label: 'Full-width', description: 'Spans both columns' }, +]; + export const PRIORITY_OPTIONS: SelectOption[] = [ { value: 'essential', label: 'Essential', description: 'Always visible (~12 attributes)' }, { value: 'recommended', label: 'Recommended', description: 'One-click expand (~30 attributes)' }, @@ -169,6 +179,7 @@ export function createInitialFormData(attribute: AttributeDefinition | null): At isMultiple: attribute.isMultiple ?? false, grouping: attribute.grouping || '', metaCategory: attribute.metaCategory || 'lifestyle_details', + layoutHint: (attribute as { layoutHint?: string }).layoutHint as AttributeFormData['layoutHint'] || '', priority: (attribute as { priority?: string }).priority || 'optional', displayOrder: attribute.displayOrder, }; @@ -191,6 +202,7 @@ export function createInitialFormData(attribute: AttributeDefinition | null): At isMultiple: false, grouping: '', metaCategory: 'lifestyle_details', + layoutHint: '', priority: 'optional', displayOrder: 0, }; @@ -217,6 +229,7 @@ export interface AttributeDefinitionViewModelReturn { dataTypeSelectOptions: SelectOption[]; metaCategorySelectOptions: SelectOption[]; prioritySelectOptions: SelectOption[]; + layoutHintSelectOptions: SelectOption[]; // Loading/error states isPending: boolean;