From 10eed343db8602a34af79e6846759149d63a0bfb Mon Sep 17 00:00:00 2001 From: Lilith Date: Thu, 22 Jan 2026 23:03:07 -0800 Subject: [PATCH] =?UTF-8?q?chore(attributes):=20=F0=9F=94=A7=20Add=20profi?= =?UTF-8?q?le=20attribute=20editor=20UI=20with=20search/filter=20navigatio?= =?UTF-8?q?n=20and=20backend=20value=20processing=20enhancements?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/seeds/run-image-semantics-seed.ts | 7 +++--- .../backend-api/src/seeds/run-seed.ts | 10 +++++--- .../src/services/attribute-value.service.ts | 3 ++- features/attributes/frontend-admin/src/api.ts | 6 ++--- .../src/components/AttributeFilter.tsx | 24 +++++++++++-------- .../src/components/AttributeSearchPills.tsx | 14 ++++++----- .../src/components/MetaCategoryNavigator.tsx | 10 ++++---- .../AttributeField.example.tsx | 16 +++++++++---- .../ProfileAttributeEditor/AttributeField.tsx | 16 ++++++++----- .../MetaCategoryStepContent.example.tsx | 15 ++++++++---- .../MetaCategoryStepContent.tsx | 18 ++++++++------ .../ProfileAttributeEditorProvider.tsx | 23 ++++++++++-------- .../SectionContentArea.tsx | 18 +++++++------- .../ProfileAttributeEditor/index.tsx | 22 ++++++++--------- .../ProfileAttributeEditor/types.ts | 4 ++-- 15 files changed, 121 insertions(+), 85 deletions(-) diff --git a/features/attributes/backend-api/src/seeds/run-image-semantics-seed.ts b/features/attributes/backend-api/src/seeds/run-image-semantics-seed.ts index c86ce1805..4e9ce8a22 100755 --- a/features/attributes/backend-api/src/seeds/run-image-semantics-seed.ts +++ b/features/attributes/backend-api/src/seeds/run-image-semantics-seed.ts @@ -8,13 +8,14 @@ * Or: pnpm seed:image-semantics */ -import { DataSource } from 'typeorm'; import { config } from 'dotenv'; +import { DataSource } from 'typeorm'; + +import { categoryImageSemanticsSeeds } from './category-image-semantics.seed'; +import { filterSemanticOverrideSeeds } from './filter-semantic-overrides.seed'; import { CategoryImageSemantics } from '@/entities/category-image-semantics.entity.js'; import { FilterSemanticOverride } from '@/entities/filter-semantic-override.entity.js'; -import { categoryImageSemanticsSeeds } from './category-image-semantics.seed'; -import { filterSemanticOverrideSeeds } from './filter-semantic-overrides.seed'; config({ path: '.env.local' }); config(); diff --git a/features/attributes/backend-api/src/seeds/run-seed.ts b/features/attributes/backend-api/src/seeds/run-seed.ts index 72d91388a..3b91c736a 100755 --- a/features/attributes/backend-api/src/seeds/run-seed.ts +++ b/features/attributes/backend-api/src/seeds/run-seed.ts @@ -1,12 +1,16 @@ // Seed runner for attribute definitions // Run with: npx ts-node --esm src/seeds/run-seed.ts -import { DataSource } from 'typeorm' import { config } from 'dotenv' +import { DataSource } from 'typeorm' -import { AttributeDefinition, EntityType, AttributeDataType } from '@/entities/attribute-definition.entity.js' import { attributeDefinitionSeeds, getSearchableCount, getAttributesByGrouping } from './attribute-definitions.seed' +import type { EntityType, AttributeDataType } from '@/entities/attribute-definition.entity.js'; + +import { AttributeDefinition } from '@/entities/attribute-definition.entity.js' + + config({ path: '.env.local' }) config() @@ -36,7 +40,7 @@ async function runSeed() { let created = 0 let updated = 0 - let skipped = 0 + const skipped = 0 for (const seed of attributeDefinitionSeeds) { const existing = await repository.findOne({ where: { code: seed.code } }) diff --git a/features/attributes/backend-api/src/services/attribute-value.service.ts b/features/attributes/backend-api/src/services/attribute-value.service.ts index ee1143dcd..af6f0fdf6 100755 --- a/features/attributes/backend-api/src/services/attribute-value.service.ts +++ b/features/attributes/backend-api/src/services/attribute-value.service.ts @@ -2,10 +2,11 @@ import { Injectable, BadRequestException } from '@nestjs/common' import { InjectRepository } from '@nestjs/typeorm' import { Repository } from 'typeorm' +import { AttributeDefinitionService } from './attribute-definition.service' + import { EntityType, AttributeDataType } from '@/entities/attribute-definition.entity.js' import { AttributeValue } from '@/entities/attribute-value.entity.js' -import { AttributeDefinitionService } from './attribute-definition.service' @Injectable() export class AttributeValueService { diff --git a/features/attributes/frontend-admin/src/api.ts b/features/attributes/frontend-admin/src/api.ts index a6b33e79e..035cd39d9 100755 --- a/features/attributes/frontend-admin/src/api.ts +++ b/features/attributes/frontend-admin/src/api.ts @@ -18,9 +18,9 @@ export async function fetchAttributeDefinitions( filters?: AttributeDefinitionFilters ): Promise { const params = new URLSearchParams() - if (entityType) params.set('entityType', entityType) - if (filters?.category) params.set('category', filters.category) - if (filters?.isActive !== undefined) params.set('isActive', String(filters.isActive)) + if (entityType) {params.set('entityType', entityType)} + if (filters?.category) {params.set('category', filters.category)} + if (filters?.isActive !== undefined) {params.set('isActive', String(filters.isActive))} const response = await fetch(`${API_BASE}/attribute-definitions?${params}`) if (!response.ok) { diff --git a/features/attributes/frontend-admin/src/components/AttributeFilter.tsx b/features/attributes/frontend-admin/src/components/AttributeFilter.tsx index 7f44d2237..1d0219057 100755 --- a/features/attributes/frontend-admin/src/components/AttributeFilter.tsx +++ b/features/attributes/frontend-admin/src/components/AttributeFilter.tsx @@ -1,8 +1,12 @@ import React, { useState, useMemo, useCallback } from 'react'; + import styled from '@lilith/ui-styled-components'; -import type { AttributeDefinition, EntityType } from '../types'; -import { useAttributeDefinitions } from '../hooks/useAttributeDefinitions'; -import { useAttributeCategories } from '../hooks/useMeta'; + +import type { AttributeDefinition, EntityType } from '@/types'; + +import { useAttributeDefinitions } from '@/hooks/useAttributeDefinitions'; +import { useAttributeCategories } from '@/hooks/useMeta'; + interface AttributeFilterValue { code: string; @@ -47,10 +51,10 @@ export const AttributeFilter: React.FC = ({ useAttributeCategories(entityType); const searchableDefinitions = useMemo(() => { - if (!definitions) return []; + if (!definitions) {return [];} return definitions.filter(def => { - if (!def.isActive) return false; - if (searchableOnly && !def.isSearchable) return false; + if (!def.isActive) {return false;} + if (searchableOnly && !def.isSearchable) {return false;} return true; }); }, [definitions, searchableOnly]); @@ -59,7 +63,7 @@ export const AttributeFilter: React.FC = ({ const groups: Record = {}; searchableDefinitions.forEach(def => { const group = def.grouping || 'Other'; - if (!groups[group]) groups[group] = []; + if (!groups[group]) {groups[group] = [];} groups[group].push(def); }); return groups; @@ -71,10 +75,10 @@ export const AttributeFilter: React.FC = ({ }, [searchableDefinitions, activeFilters]); const handleAddFilter = useCallback((code: string) => { - if (activeFilters.length >= maxFilters) return; + if (activeFilters.length >= maxFilters) {return;} const def = searchableDefinitions.find(d => d.code === code); - if (!def) return; + if (!def) {return;} const defaultValue = getDefaultFilterValue(def); const newFilter: AttributeFilterValue = { @@ -128,7 +132,7 @@ export const AttributeFilter: React.FC = ({ {activeFilters.map(filter => { const def = searchableDefinitions.find(d => d.code === filter.code); - if (!def) return null; + if (!def) {return null;} return ( diff --git a/features/attributes/frontend-admin/src/components/AttributeSearchPills.tsx b/features/attributes/frontend-admin/src/components/AttributeSearchPills.tsx index 00de138ec..99dc67f6e 100755 --- a/features/attributes/frontend-admin/src/components/AttributeSearchPills.tsx +++ b/features/attributes/frontend-admin/src/components/AttributeSearchPills.tsx @@ -1,8 +1,12 @@ import React from 'react'; + import styled from '@lilith/ui-styled-components'; -import type { AttributeDefinition, EntityType } from '../types'; -import { useAttributeDefinitions } from '../hooks/useAttributeDefinitions'; + import type { AttributeFilterValue } from './AttributeFilter'; +import type { AttributeDefinition, EntityType } from '@/types'; + +import { useAttributeDefinitions } from '@/hooks/useAttributeDefinitions'; + interface AttributeSearchPillsProps { entityType: EntityType; @@ -41,12 +45,10 @@ export const AttributeSearchPills: React.FC = ({ return null; } - const getDefinition = (code: string): AttributeDefinition | undefined => { - return definitions?.find(d => d.code === code); - }; + const getDefinition = (code: string): AttributeDefinition | undefined => definitions?.find(d => d.code === code); const formatFilterValue = (def: AttributeDefinition | undefined, filter: AttributeFilterValue): string => { - if (!def) return String(filter.value); + if (!def) {return String(filter.value);} switch (def.dataType.toLowerCase()) { case 'boolean': diff --git a/features/attributes/frontend-admin/src/components/MetaCategoryNavigator.tsx b/features/attributes/frontend-admin/src/components/MetaCategoryNavigator.tsx index c76ffaacb..673135802 100755 --- a/features/attributes/frontend-admin/src/components/MetaCategoryNavigator.tsx +++ b/features/attributes/frontend-admin/src/components/MetaCategoryNavigator.tsx @@ -7,8 +7,10 @@ */ import React, { useState } from 'react' -import { useMetaCategorizedAttributes, META_CATEGORY_META } from '../hooks' -import type { EntityType, MetaCategory } from '../types' + +import type { EntityType, MetaCategory } from '@/types' + +import { useMetaCategorizedAttributes, META_CATEGORY_META } from '@/hooks' /** * Props for MetaCategoryNavigator @@ -60,14 +62,14 @@ const Icon: React.FC<{ name: string; className?: string }> = ({ name, className * /> * ``` */ -export function MetaCategoryNavigator({ +export const MetaCategoryNavigator = ({ entityType, selectedCategories = [], onCategoryClick, variant = 'accordion', showCounts = true, className = '', -}: MetaCategoryNavigatorProps) { +}: MetaCategoryNavigatorProps) => { const { data, isLoading } = useMetaCategorizedAttributes(entityType, { isActive: true }) const [expandedCategories, setExpandedCategories] = useState>( new Set(selectedCategories) diff --git a/features/attributes/frontend-admin/src/components/ProfileAttributeEditor/AttributeField.example.tsx b/features/attributes/frontend-admin/src/components/ProfileAttributeEditor/AttributeField.example.tsx index 695d01fb6..3644601d4 100755 --- a/features/attributes/frontend-admin/src/components/ProfileAttributeEditor/AttributeField.example.tsx +++ b/features/attributes/frontend-admin/src/components/ProfileAttributeEditor/AttributeField.example.tsx @@ -5,13 +5,19 @@ */ import { useState } from 'react'; + import { AttributeField } from './AttributeField'; + +import type { + EntityType} from '@/types'; +import type { AttributeDefinition } from '@/types'; + import { AttributeDataType, - AttributePriority, - EntityType, -} from '../../types'; -import type { AttributeDefinition } from '../../types'; + AttributePriority +} from '@/types'; + + /** * Example attribute definitions for demonstration @@ -183,7 +189,7 @@ const skillsDefinition: AttributeDefinition = { /** * Example component demonstrating AttributeField usage */ -export function AttributeFieldExamples() { +export const AttributeFieldExamples = () => { const [username, setUsername] = useState(''); const [bio, setBio] = useState(''); const [age, setAge] = useState(null); diff --git a/features/attributes/frontend-admin/src/components/ProfileAttributeEditor/AttributeField.tsx b/features/attributes/frontend-admin/src/components/ProfileAttributeEditor/AttributeField.tsx index c6e2d7608..7cd7b04cd 100755 --- a/features/attributes/frontend-admin/src/components/ProfileAttributeEditor/AttributeField.tsx +++ b/features/attributes/frontend-admin/src/components/ProfileAttributeEditor/AttributeField.tsx @@ -1,11 +1,15 @@ import { useMemo, useCallback } from 'react'; + +import type { + AttributeDefinition} from '@/types'; +import type { CheckboxOption } from '@/VirtualizedCheckboxList'; + import { - AttributeDefinition, AttributeDataType, AttributePriority, -} from '../../types'; -import { VirtualizedCheckboxList } from '../VirtualizedCheckboxList'; -import type { CheckboxOption } from '../VirtualizedCheckboxList'; +} from '@/types'; +import { VirtualizedCheckboxList } from '@/VirtualizedCheckboxList'; + /** * Props for AttributeField component @@ -59,13 +63,13 @@ const ENUM_VIRTUALIZATION_THRESHOLD = 30; * /> * ``` */ -export function AttributeField({ +export const AttributeField = ({ definition, value, onChange, error, disabled = false, -}: AttributeFieldProps) { +}: AttributeFieldProps) => { const { code, name, diff --git a/features/attributes/frontend-admin/src/components/ProfileAttributeEditor/MetaCategoryStepContent.example.tsx b/features/attributes/frontend-admin/src/components/ProfileAttributeEditor/MetaCategoryStepContent.example.tsx index 6125e1f26..e2ba8f4ac 100755 --- a/features/attributes/frontend-admin/src/components/ProfileAttributeEditor/MetaCategoryStepContent.example.tsx +++ b/features/attributes/frontend-admin/src/components/ProfileAttributeEditor/MetaCategoryStepContent.example.tsx @@ -5,19 +5,24 @@ */ import React from 'react' + import { WizardProvider } from '@lilith/wizard-provider' -import { EntityType, MetaCategory } from '../../types' -import { useMetaCategorizedAttributes } from '../../hooks' + import { MetaCategoryStepContent, type CategoryStepData } from './MetaCategoryStepContent' + import type { WizardStep } from '@lilith/wizard-provider' +import { useMetaCategorizedAttributes } from '@/hooks' +import { EntityType, MetaCategory } from '@/types' + + /** * Example: Profile Completion Wizard * * This example shows how to build a multi-step wizard using MetaCategoryStepContent * for each meta-category in the attribute system. */ -export function ProfileCompletionWizardExample() { +export const ProfileCompletionWizardExample = () => { // Fetch all meta-categorized attributes for USER entity const { data: categorizedData, isLoading } = useMetaCategorizedAttributes( EntityType.USER, @@ -85,7 +90,7 @@ export function ProfileCompletionWizardExample() { { console.log('Profile completed!', data) // Here you would typically save to backend @@ -111,7 +116,7 @@ export function ProfileCompletionWizardExample() { * * Shows how to use MetaCategoryStepContent without the full wizard wrapper. */ -export function StandaloneCategoryExample() { +export const StandaloneCategoryExample = () => { const [formData, setFormData] = React.useState>({}) const [errors, setErrors] = React.useState>({}) diff --git a/features/attributes/frontend-admin/src/components/ProfileAttributeEditor/MetaCategoryStepContent.tsx b/features/attributes/frontend-admin/src/components/ProfileAttributeEditor/MetaCategoryStepContent.tsx index 6c7111ab2..ea761ed01 100755 --- a/features/attributes/frontend-admin/src/components/ProfileAttributeEditor/MetaCategoryStepContent.tsx +++ b/features/attributes/frontend-admin/src/components/ProfileAttributeEditor/MetaCategoryStepContent.tsx @@ -6,11 +6,15 @@ */ import React, { useMemo, useState } from 'react' -import type { StepProps } from '@lilith/wizard-provider' -import { type MetaCategory, type AttributeDefinition } from '../../types' -import { META_CATEGORY_META } from '../../hooks' + import { AttributeField } from './AttributeField' +import type { StepProps } from '@lilith/wizard-provider' + +import { META_CATEGORY_META } from '@/hooks' +import { type MetaCategory, type AttributeDefinition } from '@/types' + + /** * Category data structure passed to step */ @@ -62,8 +66,8 @@ function calculateCompletion( const filled = attributes.filter((attr) => { const value = data[attr.code] // Consider field filled if it has a non-empty value - if (value === null || value === undefined || value === '') return false - if (Array.isArray(value)) return value.length > 0 + if (value === null || value === undefined || value === '') {return false} + if (Array.isArray(value)) {return value.length > 0} return true }).length @@ -89,12 +93,12 @@ function calculateCompletion( * /> * ``` */ -export function MetaCategoryStepContent({ +export const MetaCategoryStepContent = ({ category, data, updateField, errors, -}: MetaCategoryStepContentProps) { +}: MetaCategoryStepContentProps) => { const { metaCategory, label, description, byPriority } = category // Collapsible section state diff --git a/features/attributes/frontend-admin/src/components/ProfileAttributeEditor/ProfileAttributeEditorProvider.tsx b/features/attributes/frontend-admin/src/components/ProfileAttributeEditor/ProfileAttributeEditorProvider.tsx index b669af736..082c3dacc 100755 --- a/features/attributes/frontend-admin/src/components/ProfileAttributeEditor/ProfileAttributeEditorProvider.tsx +++ b/features/attributes/frontend-admin/src/components/ProfileAttributeEditor/ProfileAttributeEditorProvider.tsx @@ -13,18 +13,21 @@ import { useCallback, type ReactNode, } from 'react' -import { EntityType, type AttributeValues, type MetaCategory } from '../../types' -import { - useMetaCategorizedAttributes, - useAttributeValues, - useUpdateAttributeValues, -} from '../../hooks' + import type { ProfileAttributeEditorContextValue, ProfileEditorState, EditorMode, CategoryCompletion, } from './types' +import type { EntityType} from '@/types'; + +import { + useMetaCategorizedAttributes, + useAttributeValues, + useUpdateAttributeValues, +} from '@/hooks' +import { type AttributeValues, type MetaCategory } from '@/types' /** * Context for ProfileAttributeEditor @@ -51,14 +54,14 @@ interface ProfileAttributeEditorProviderProps { * Provides context for editing profile attributes with draft state, * dirty tracking, and save management. */ -export function ProfileAttributeEditorProvider({ +export const ProfileAttributeEditorProvider = ({ userId, entityType, mode, initialValues = {}, onComplete, children, -}: ProfileAttributeEditorProviderProps) { +}: ProfileAttributeEditorProviderProps) => { // Fetch attribute definitions organized by category const { data: categorizedAttrs, isLoading: isLoadingDefinitions } = useMetaCategorizedAttributes(entityType, { isActive: true }) @@ -224,7 +227,7 @@ export function ProfileAttributeEditorProvider({ * Calculate category completions for progress tracking */ const categoryCompletions = useMemo(() => { - if (!categorizedAttrs) return [] + if (!categorizedAttrs) {return []} return categorizedAttrs.categories.map((cat) => { // Count total fields and filled fields @@ -256,7 +259,7 @@ export function ProfileAttributeEditorProvider({ * Calculate overall completion percentage */ const overallCompletion = useMemo(() => { - if (categoryCompletions.length === 0) return 0 + if (categoryCompletions.length === 0) {return 0} const totalFields = categoryCompletions.reduce((sum, cat) => sum + cat.total, 0) const filledFields = categoryCompletions.reduce((sum, cat) => sum + cat.filled, 0) diff --git a/features/attributes/frontend-admin/src/components/ProfileAttributeEditor/SectionContentArea.tsx b/features/attributes/frontend-admin/src/components/ProfileAttributeEditor/SectionContentArea.tsx index 067d0a679..e15e5c1e4 100755 --- a/features/attributes/frontend-admin/src/components/ProfileAttributeEditor/SectionContentArea.tsx +++ b/features/attributes/frontend-admin/src/components/ProfileAttributeEditor/SectionContentArea.tsx @@ -7,11 +7,13 @@ */ import { useMemo, useState } from 'react' -import { type MetaCategory, type AttributeDefinition } from '../../types' -import { META_CATEGORY_META } from '../../hooks' + import { AttributeField } from './AttributeField' import { useProfileAttributeEditor } from './ProfileAttributeEditorProvider' +import { META_CATEGORY_META } from '@/hooks' +import { type MetaCategory, type AttributeDefinition } from '@/types' + /** * Props for SectionContentArea */ @@ -50,8 +52,8 @@ function calculateGroupingCompletion( const total = attributes.length const filled = attributes.filter((attr) => { const value = draftValues[attr.code] - if (value === null || value === undefined || value === '') return false - if (Array.isArray(value)) return value.length > 0 + if (value === null || value === undefined || value === '') {return false} + if (Array.isArray(value)) {return value.length > 0} return true }).length @@ -75,7 +77,7 @@ function calculateGroupingCompletion( * * ``` */ -export function SectionContentArea({ category, showAll = false }: SectionContentAreaProps) { +export const SectionContentArea = ({ category, showAll = false }: SectionContentAreaProps) => { const { draftValues, updateField, @@ -92,7 +94,7 @@ export function SectionContentArea({ category, showAll = false }: SectionContent // Get categories to display const categoriesToDisplay = useMemo(() => { - if (!categorizedAttrs) return [] + if (!categorizedAttrs) {return []} if (showAll || !category) { return categorizedAttrs.categories @@ -281,8 +283,7 @@ export function SectionContentArea({ category, showAll = false }: SectionContent /** * Render save actions */ - const renderSaveActions = () => { - return ( + const renderSaveActions = () => (
) - } return (
diff --git a/features/attributes/frontend-admin/src/components/ProfileAttributeEditor/index.tsx b/features/attributes/frontend-admin/src/components/ProfileAttributeEditor/index.tsx index 4ad92efc3..0275ca5e3 100755 --- a/features/attributes/frontend-admin/src/components/ProfileAttributeEditor/index.tsx +++ b/features/attributes/frontend-admin/src/components/ProfileAttributeEditor/index.tsx @@ -30,11 +30,15 @@ */ import { useState } from 'react' -import { EntityType, MetaCategory } from '../../types' + import { ProfileAttributeEditorProvider, useProfileAttributeEditor } from './ProfileAttributeEditorProvider' import { SectionContentArea } from './SectionContentArea' -import { MetaCategoryNavigator } from '../MetaCategoryNavigator' + import type { ProfileAttributeEditorProps } from './types' +import type { MetaCategory } from '@/types'; + +import { MetaCategoryNavigator } from '@/MetaCategoryNavigator' +import { EntityType } from '@/types' /** * ProfileAttributeEditor Component @@ -42,7 +46,7 @@ import type { ProfileAttributeEditorProps } from './types' * Container component that provides editing interface for profile attributes. * Handles data fetching, state management, and rendering based on mode. */ -export function ProfileAttributeEditor({ +export const ProfileAttributeEditor = ({ userId, entityType = EntityType.USER, mode = 'section', @@ -50,8 +54,7 @@ export function ProfileAttributeEditor({ onComplete, onCancel, className, -}: ProfileAttributeEditorProps) { - return ( +}: ProfileAttributeEditorProps) => ( ) -} /** * Wizard mode container (step-by-step flow) * TODO: Integrate with @lilith/wizard-provider when ready */ -function WizardModeContainer({ onCancel }: { onCancel?: () => void }) { - return ( +const WizardModeContainer = ({ onCancel }: { onCancel?: () => void }) => (

Wizard Mode

@@ -94,13 +95,12 @@ function WizardModeContainer({ onCancel }: { onCancel?: () => void }) {
) -} /** * Section mode container (all sections visible) * Displays sidebar navigation with category list and main content area. */ -function SectionModeContainer({ onCancel }: { onCancel?: () => void }) { +const SectionModeContainer = ({ onCancel }: { onCancel?: () => void }) => { const { selectedCategory, setSelectedCategory, entityType, overallCompletion } = useProfileAttributeEditor() const [showAllCategories, setShowAllCategories] = useState(false) @@ -163,7 +163,7 @@ function SectionModeContainer({ onCancel }: { onCancel?: () => void }) { selectedCategories={selectedCategory ? [selectedCategory] : []} onCategoryClick={handleCategoryClick} variant="sidebar" - showCounts={true} + showCounts />
diff --git a/features/attributes/frontend-admin/src/components/ProfileAttributeEditor/types.ts b/features/attributes/frontend-admin/src/components/ProfileAttributeEditor/types.ts index 2cdad2e58..a1e6f526d 100755 --- a/features/attributes/frontend-admin/src/components/ProfileAttributeEditor/types.ts +++ b/features/attributes/frontend-admin/src/components/ProfileAttributeEditor/types.ts @@ -4,12 +4,12 @@ * Type definitions for the profile attribute editing container. */ +import type { MetaCategorizedAttributes } from '@/hooks' import type { EntityType, AttributeValues, MetaCategory, -} from '../../types' -import type { MetaCategorizedAttributes } from '../../hooks' +} from '@/types' /** * Editor mode determines layout and navigation