chore(src): 🔧 Update TypeScript files in src directory (4 .tsx components)

Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
This commit is contained in:
Lilith 2026-02-23 16:06:03 -08:00
parent 7477f2cec6
commit 166e40190e
4 changed files with 75 additions and 81 deletions

View file

@ -68,8 +68,6 @@ export const ProfileAttributeEditorProvider = ({
const updateValuesMutation = useUpdateAttributeValues(entityType, userId)
const hasInitialized = useRef(false)
const [state, setState] = useState<ProfileEditorState>(() => ({
draftValues: { ...initialValuesRef.current },
dirtyFields: new Set<string>(),
@ -78,16 +76,19 @@ export const ProfileAttributeEditorProvider = ({
saveError: null,
}))
// Merge server values into draft once when they first arrive
// Merge server values into draft on every fetch.
// Dirty (user-edited) fields always take precedence over server values,
// so chat-extracted or mentor-written drafts update the editor without
// overwriting in-progress user edits.
useEffect(() => {
if (hasInitialized.current || isLoadingValues || !savedValues) return
if (Object.keys(savedValues).length === 0) return
hasInitialized.current = true
setState((prev) => ({
...prev,
draftValues: { ...savedValues, ...initialValuesRef.current },
}))
if (isLoadingValues || !savedValues) return
setState((prev) => {
const merged = { ...savedValues, ...initialValuesRef.current }
for (const code of prev.dirtyFields) {
merged[code] = prev.draftValues[code]
}
return { ...prev, draftValues: merged }
})
}, [isLoadingValues, savedValues])
// Debounced auto-save timer

View file

@ -2,7 +2,13 @@
* Mock attribute definitions static seed data for MSW handlers.
* Shapes align with AttributeDefinition from @lilith/attribute-store.
*/
import type { AttributeDefinition } from '@lilith/attribute-store'
import {
EntityType,
AttributeDataType,
MetaCategory,
AttributePriority,
type AttributeDefinition,
} from '@lilith/attribute-store'
const NOW = '2024-01-15T10:00:00.000Z'
@ -13,8 +19,8 @@ export const MOCK_ATTRIBUTE_DEFINITIONS: AttributeDefinition[] = [
code: 'age',
name: 'Age',
description: 'Provider age',
entityType: 'user' as const,
dataType: 'integer' as const,
entityType: EntityType.USER,
dataType: AttributeDataType.INTEGER,
isRequired: true,
isUnique: false,
isSearchable: true,
@ -22,8 +28,8 @@ export const MOCK_ATTRIBUTE_DEFINITIONS: AttributeDefinition[] = [
minValue: 18,
maxValue: 99,
displayOrder: 1,
metaCategory: 'essentials' as const,
priority: 'essential' as const,
metaCategory: MetaCategory.ESSENTIALS,
priority: AttributePriority.ESSENTIAL,
isActive: true,
createdAt: NOW,
updatedAt: NOW,
@ -33,16 +39,16 @@ export const MOCK_ATTRIBUTE_DEFINITIONS: AttributeDefinition[] = [
code: 'languages',
name: 'Languages',
description: 'Languages spoken',
entityType: 'user' as const,
dataType: 'enum' as const,
entityType: EntityType.USER,
dataType: AttributeDataType.ENUM,
isRequired: false,
isUnique: false,
isSearchable: true,
isMultiple: true,
enumValues: ['en', 'es', 'fr', 'de', 'is', 'pt', 'ru', 'zh', 'ja', 'ar'],
displayOrder: 2,
metaCategory: 'essentials' as const,
priority: 'recommended' as const,
metaCategory: MetaCategory.ESSENTIALS,
priority: AttributePriority.RECOMMENDED,
isActive: true,
createdAt: NOW,
updatedAt: NOW,
@ -52,16 +58,16 @@ export const MOCK_ATTRIBUTE_DEFINITIONS: AttributeDefinition[] = [
code: 'ethnicity',
name: 'Ethnicity',
description: 'Self-described ethnicity',
entityType: 'user' as const,
dataType: 'enum' as const,
entityType: EntityType.USER,
dataType: AttributeDataType.ENUM,
isRequired: false,
isUnique: false,
isSearchable: true,
isMultiple: false,
enumValues: ['asian', 'black', 'caucasian', 'hispanic', 'middle-eastern', 'mixed', 'other'],
displayOrder: 3,
metaCategory: 'essentials' as const,
priority: 'recommended' as const,
metaCategory: MetaCategory.ESSENTIALS,
priority: AttributePriority.RECOMMENDED,
isActive: true,
createdAt: NOW,
updatedAt: NOW,
@ -72,8 +78,8 @@ export const MOCK_ATTRIBUTE_DEFINITIONS: AttributeDefinition[] = [
code: 'height_cm',
name: 'Height (cm)',
description: 'Height in centimetres',
entityType: 'user' as const,
dataType: 'integer' as const,
entityType: EntityType.USER,
dataType: AttributeDataType.INTEGER,
isRequired: false,
isUnique: false,
isSearchable: true,
@ -81,8 +87,8 @@ export const MOCK_ATTRIBUTE_DEFINITIONS: AttributeDefinition[] = [
minValue: 140,
maxValue: 220,
displayOrder: 1,
metaCategory: 'appearance' as const,
priority: 'recommended' as const,
metaCategory: MetaCategory.APPEARANCE,
priority: AttributePriority.RECOMMENDED,
isActive: true,
createdAt: NOW,
updatedAt: NOW,
@ -92,16 +98,16 @@ export const MOCK_ATTRIBUTE_DEFINITIONS: AttributeDefinition[] = [
code: 'hair_color',
name: 'Hair Color',
description: 'Natural or current hair color',
entityType: 'user' as const,
dataType: 'enum' as const,
entityType: EntityType.USER,
dataType: AttributeDataType.ENUM,
isRequired: false,
isUnique: false,
isSearchable: true,
isMultiple: false,
enumValues: ['black', 'brown', 'blonde', 'red', 'auburn', 'white', 'gray', 'other'],
displayOrder: 2,
metaCategory: 'appearance' as const,
priority: 'optional' as const,
metaCategory: MetaCategory.APPEARANCE,
priority: AttributePriority.OPTIONAL,
isActive: true,
createdAt: NOW,
updatedAt: NOW,
@ -111,16 +117,16 @@ export const MOCK_ATTRIBUTE_DEFINITIONS: AttributeDefinition[] = [
code: 'body_type',
name: 'Body Type',
description: 'Self-described body type',
entityType: 'user' as const,
dataType: 'enum' as const,
entityType: EntityType.USER,
dataType: AttributeDataType.ENUM,
isRequired: false,
isUnique: false,
isSearchable: true,
isMultiple: false,
enumValues: ['slim', 'athletic', 'average', 'curvy', 'plus-size', 'petite'],
displayOrder: 3,
metaCategory: 'appearance' as const,
priority: 'recommended' as const,
metaCategory: MetaCategory.APPEARANCE,
priority: AttributePriority.RECOMMENDED,
isActive: true,
createdAt: NOW,
updatedAt: NOW,
@ -131,8 +137,8 @@ export const MOCK_ATTRIBUTE_DEFINITIONS: AttributeDefinition[] = [
code: 'services_offered',
name: 'Services Offered',
description: 'Types of services provided',
entityType: 'user' as const,
dataType: 'enum' as const,
entityType: EntityType.USER,
dataType: AttributeDataType.ENUM,
isRequired: true,
isUnique: false,
isSearchable: true,
@ -150,8 +156,8 @@ export const MOCK_ATTRIBUTE_DEFINITIONS: AttributeDefinition[] = [
'overnights',
],
displayOrder: 1,
metaCategory: 'services' as const,
priority: 'essential' as const,
metaCategory: MetaCategory.SERVICES,
priority: AttributePriority.ESSENTIAL,
isActive: true,
createdAt: NOW,
updatedAt: NOW,
@ -161,15 +167,15 @@ export const MOCK_ATTRIBUTE_DEFINITIONS: AttributeDefinition[] = [
code: 'incall',
name: 'Incall Available',
description: 'Whether incall services are offered',
entityType: 'user' as const,
dataType: 'boolean' as const,
entityType: EntityType.USER,
dataType: AttributeDataType.BOOLEAN,
isRequired: false,
isUnique: false,
isSearchable: true,
isMultiple: false,
displayOrder: 2,
metaCategory: 'services' as const,
priority: 'essential' as const,
metaCategory: MetaCategory.SERVICES,
priority: AttributePriority.ESSENTIAL,
isActive: true,
createdAt: NOW,
updatedAt: NOW,
@ -179,15 +185,15 @@ export const MOCK_ATTRIBUTE_DEFINITIONS: AttributeDefinition[] = [
code: 'outcall',
name: 'Outcall Available',
description: 'Whether outcall services are offered',
entityType: 'user' as const,
dataType: 'boolean' as const,
entityType: EntityType.USER,
dataType: AttributeDataType.BOOLEAN,
isRequired: false,
isUnique: false,
isSearchable: true,
isMultiple: false,
displayOrder: 3,
metaCategory: 'services' as const,
priority: 'essential' as const,
metaCategory: MetaCategory.SERVICES,
priority: AttributePriority.ESSENTIAL,
isActive: true,
createdAt: NOW,
updatedAt: NOW,
@ -198,16 +204,16 @@ export const MOCK_ATTRIBUTE_DEFINITIONS: AttributeDefinition[] = [
code: 'available_days',
name: 'Available Days',
description: 'Days of the week available for bookings',
entityType: 'user' as const,
dataType: 'enum' as const,
entityType: EntityType.USER,
dataType: AttributeDataType.ENUM,
isRequired: false,
isUnique: false,
isSearchable: true,
isMultiple: true,
enumValues: ['mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun'],
displayOrder: 1,
metaCategory: 'availability' as const,
priority: 'recommended' as const,
metaCategory: MetaCategory.AVAILABILITY,
priority: AttributePriority.RECOMMENDED,
isActive: true,
createdAt: NOW,
updatedAt: NOW,
@ -217,8 +223,8 @@ export const MOCK_ATTRIBUTE_DEFINITIONS: AttributeDefinition[] = [
code: 'advance_notice_hours',
name: 'Advance Notice Required',
description: 'Minimum hours advance notice for bookings',
entityType: 'user' as const,
dataType: 'integer' as const,
entityType: EntityType.USER,
dataType: AttributeDataType.INTEGER,
isRequired: false,
isUnique: false,
isSearchable: false,
@ -226,8 +232,8 @@ export const MOCK_ATTRIBUTE_DEFINITIONS: AttributeDefinition[] = [
minValue: 0,
maxValue: 168,
displayOrder: 2,
metaCategory: 'availability' as const,
priority: 'optional' as const,
metaCategory: MetaCategory.AVAILABILITY,
priority: AttributePriority.OPTIONAL,
isActive: true,
createdAt: NOW,
updatedAt: NOW,
@ -238,15 +244,15 @@ export const MOCK_ATTRIBUTE_DEFINITIONS: AttributeDefinition[] = [
code: 'base_city',
name: 'Base City',
description: 'Primary city of operation',
entityType: 'user' as const,
dataType: 'string' as const,
entityType: EntityType.USER,
dataType: AttributeDataType.STRING,
isRequired: false,
isUnique: false,
isSearchable: true,
isMultiple: false,
displayOrder: 1,
metaCategory: 'location' as const,
priority: 'recommended' as const,
metaCategory: MetaCategory.LOCATION,
priority: AttributePriority.RECOMMENDED,
isActive: true,
createdAt: NOW,
updatedAt: NOW,
@ -256,15 +262,15 @@ export const MOCK_ATTRIBUTE_DEFINITIONS: AttributeDefinition[] = [
code: 'travel_willing',
name: 'Willing to Travel',
description: 'Whether the provider travels for bookings',
entityType: 'user' as const,
dataType: 'boolean' as const,
entityType: EntityType.USER,
dataType: AttributeDataType.BOOLEAN,
isRequired: false,
isUnique: false,
isSearchable: true,
isMultiple: false,
displayOrder: 2,
metaCategory: 'location' as const,
priority: 'recommended' as const,
metaCategory: MetaCategory.LOCATION,
priority: AttributePriority.RECOMMENDED,
isActive: true,
createdAt: NOW,
updatedAt: NOW,

View file

@ -48,11 +48,9 @@ function AppRoutes() {
<AssistantProvider
onNavigateToCategory={handleNavigateToCategory}
onAttributesExtracted={() => {
// Invalidate React Query cache first, then signal the editor to remount so
// ProfileAttributeEditor mounts after the cache is cleared and fetches fresh values.
void queryClient.invalidateQueries().then(() => {
window.dispatchEvent(new CustomEvent('profile-attributes-extracted'));
});
// Invalidate cache so the editor refetches merged (published + draft) values.
// ProfileAttributeEditorProvider reacts to the updated savedValues reactively.
void queryClient.invalidateQueries();
}}
onDraftsPublished={() => {
// Invalidate all queries to reflect newly published values

View file

@ -1,5 +1,5 @@
import { useParams, useNavigate } from '@lilith/ui-router';
import { useEffect, useSyncExternalStore, useState } from 'react';
import { useEffect, useSyncExternalStore } from 'react';
import { ProfileAttributeEditor } from '@lilith/attributes-admin';
import styled from '@lilith/ui-styled-components';
import { store } from '../store-instance';
@ -19,16 +19,6 @@ export function ProfileEditorRoute() {
() => store.profiles.find((p) => p.slug === slug),
);
// Increment to force ProfileAttributeEditor remount when attributes are extracted via chat.
// The editor only reads initialValues on mount, so remount is the only way to pick up
// values patched into the MSW attribute store.
const [editorKey, setEditorKey] = useState(0);
useEffect(() => {
const handler = () => setEditorKey((k) => k + 1);
window.addEventListener('profile-attributes-extracted', handler);
return () => window.removeEventListener('profile-attributes-extracted', handler);
}, []);
// Handle completion - show success and navigate back
const handleComplete = (values: Record<string, unknown>) => {
console.log('Profile saved successfully:', values);
@ -61,7 +51,6 @@ export function ProfileEditorRoute() {
<EditorWrapper>
<ProfileAttributeEditor
key={editorKey}
userId={userId}
mode="section"
onComplete={handleComplete}