Skip to content

Commit 50b0d55

Browse files
author
Herve Tribouilloy
committed
fix(intent): prevent unnecessary AI calls for filter-only interactions
- only trigger AI interpretation when intent text is present - allow filter selections to directly drive product suggestions - stabilise handleAsk flow (no conditional hook usage) - improve UX consistency between typed and structured intent
1 parent 2a07428 commit 50b0d55

32 files changed

Lines changed: 307 additions & 150 deletions

vite_project/package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

vite_project/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "widget-intent-discovery",
33
"private": true,
4-
"version": "0.7.1",
4+
"version": "1.0.0",
55
"type": "module",
66
"scripts": {
77
"dev": "vite",

vite_project/src/IntentDiscoveryWidgetWrapper.tsx

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {SystemStateProvider} from "./state/System/SystemStateProvider.tsx";
44
import {IntentLookup} from "./components/IntentLookup.tsx";
55
import {TranslationStateProvider} from "./state/Translation/TranslationStateProvider.tsx";
66
import {SpinnerOverlay} from "./components/global/SpinnerOverlay.tsx";
7+
import {IntentStateProvider} from "./state/Intent/IntentStateProvider.tsx";
78

89
type Props = {
910
host: HTMLElement;
@@ -16,11 +17,13 @@ export const IntentDiscoveryWidgetWrapper = ({ host }: Props) => {
1617
if (error) return <ErrorState error={error} />
1718

1819
return <SystemStateProvider config={config.integrations} store={config.storeCode}>
19-
<TranslationStateProvider translations={config.translations}>
20-
<div className="intent-widget-container">
21-
{loading ? <SpinnerOverlay /> : <IntentLookup config={config} />}
22-
</div>
23-
</TranslationStateProvider>
24-
</SystemStateProvider>
20+
<IntentStateProvider>
21+
<TranslationStateProvider translations={config.translations}>
22+
<div className="intent-widget-container">
23+
{loading ? <SpinnerOverlay /> : <IntentLookup config={config} />}
24+
</div>
25+
</TranslationStateProvider>
26+
</IntentStateProvider>
27+
</SystemStateProvider>
2528
};
2629

vite_project/src/components/AttributeLayer/AttributeSelectorLayer.tsx

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,25 +6,23 @@ import { useTranslationState } from "../../state/Translation/useTranslationState
66
import type { IntentDiscoveryDataConfig } from "../../domain/intent-discovery.types.ts";
77
import type { MagentoAggregation } from "../../hooks/infra/useProductAttributeLayer.tsx";
88
import { useInteractionState } from "../../state/Interaction/useInteractionState.ts";
9-
import {useSystemState} from "../../state/System/useSystemState.ts";
109
import {NoResult} from "../global/NoResult.tsx";
10+
import {useIntentState} from "../../state/Intent/useIntentState.ts";
1111

1212
type Props = {
1313
isDisabled: boolean
1414
aggregations: MagentoAggregation[]
15-
filteredAggregations: MagentoAggregation[]
1615
config: IntentDiscoveryDataConfig
1716
}
1817

1918
export const AttributeSelectorLayer = ({
2019
isDisabled,
2120
aggregations,
22-
filteredAggregations,
2321
config
2422
}: Props) => {
2523
const { setActiveAttribute } = useInteractionState();
2624
const [showAll, setShowAll] = useState(false);
27-
const {intentState} = useSystemState()
25+
const {intentState} = useIntentState()
2826
const { interactionState } = useInteractionState()
2927

3028
const allAttributes = useIntentAttributes(aggregations, config)

vite_project/src/components/AttributeLayer/AttributeTile.tsx

Lines changed: 28 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,24 +5,36 @@ import {decodeHtmlEntities} from "../../lib/string.ts";
55
type AttributeTileProps = {
66
attr: MagentoAggregation;
77
isSelected: boolean;
8-
value?: string;
8+
value?: string[];
99
onClick: () => void;
1010
};
1111

12-
export const AttributeTile = ({ attr, isSelected, value, onClick }: AttributeTileProps) => (
13-
<div
14-
className="choice-tile"
15-
data-intent-card={attr.attribute_code}
16-
data-intent-active={isSelected}
17-
data-intent-activated={value !== ''}
18-
onClick={onClick}
19-
>
20-
<span className={`choice-tile__label ${isSelected ? 'choice-tile__label--selected' : ''}`}>
21-
{attr.label}
22-
</span>
12+
export const AttributeTile = ({ attr, isSelected, value, onClick }: AttributeTileProps) => {
13+
const visible = value && value.slice(0, 1) || []
14+
const remaining = value && visible && value?.length - visible.length || 0
2315

24-
{value && <span className="choice-tile__info">{decodeHtmlEntities(value)}</span>}
16+
return (
17+
<div
18+
className="choice-tile"
19+
data-intent-card={attr.attribute_code}
20+
data-intent-active={isSelected}
21+
data-intent-activated={value && value?.length > 0}
22+
onClick={onClick}
23+
>
24+
<span className={`choice-tile__label ${isSelected ? 'choice-tile__label--selected' : ''}`}>
25+
{attr.label}
26+
</span>
2527

26-
<Icon attribute_code={attr.attribute_code} />
27-
</div>
28-
);
28+
{value && <span className="choice-tile__info">
29+
{visible.map(v => (
30+
<span key={v} className="badge">{decodeHtmlEntities(v)}</span>
31+
))}
32+
{remaining > 0 && (
33+
<span className="badge badge--more">+{remaining}</span>
34+
)}
35+
</span>}
36+
37+
<Icon attribute_code={attr.attribute_code}/>
38+
</div>
39+
)
40+
}

vite_project/src/components/FinderWidget/StepFinder.tsx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import { unescapeHtml } from "../../lib/string.ts";
22
import { useFindAttributeOptionsByCode } from "../../hooks/domain/useFindAttributeOptionsByCode.tsx";
33
import type {MagentoAggregationOption, MagentoProducts} from "../../hooks/infra/useProductAttributeLayer.tsx";
4-
import {useSystemState} from "../../state/System/useSystemState.ts";
54
import {useInteractionState} from "../../state/Interaction/useInteractionState.ts";
65
import {activity} from "../../activity";
6+
import {useIntentState} from "../../state/Intent/useIntentState.ts";
77

88
interface StepFinderProps {
99
optionCode: string
@@ -13,8 +13,7 @@ interface StepFinderProps {
1313
export const StepFinder: React.FC<StepFinderProps> = ({ optionCode, attributeLayerData }: StepFinderProps) => {
1414
const { setActiveAttribute, setFocusedOption } = useInteractionState()
1515
const { attributeData } = useFindAttributeOptionsByCode(optionCode, attributeLayerData)
16-
const { setPreference } = useSystemState()
17-
const {intentState} = useSystemState()
16+
const { setPreference, intentState } = useIntentState()
1817

1918
const handleOnClick = async (option: MagentoAggregationOption) => {
2019
setActiveAttribute(optionCode);

vite_project/src/components/IntentDiscovery/AttributeLayer.tsx

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,13 @@ import {useAskAi} from "../../hooks/domain/useAiInterpretation.tsx";
66
import {AttributeSelectorLayer} from "../AttributeLayer/AttributeSelectorLayer.tsx";
77
import {IntentExplanation} from "../AttributeLayer/IntentExplanation.tsx";
88
import {SearchSpinnerOverlay} from "../global/SearchSpinnerOverlay.tsx";
9+
import {useIntentState} from "../../state/Intent/useIntentState.ts";
910

1011
type Props = {
1112
config: IntentDiscoveryDataConfig
1213
intent: IntentControllerState
1314
searchPossible: boolean
1415
aggregations: MagentoAggregation[],
15-
filteredAggregations: MagentoAggregation[],
1616
disabled: boolean
1717
}
1818

@@ -21,17 +21,25 @@ export const AttributeLayer = ({
2121
intent,
2222
searchPossible,
2323
aggregations,
24-
filteredAggregations,
2524
disabled
2625
}: Props) => {
2726
const [loading, setLoading] = useState(false);
27+
const { setIntentStatus } = useIntentState()
2828

29-
const handleAsk = useAskAi({
29+
const askAi = useAskAi({
3030
intent,
3131
aggregations,
3232
config,
3333
setLoading
34-
});
34+
})
35+
36+
const handleAsk = () => {
37+
if (intent.text?.trim()) {
38+
askAi()
39+
} else {
40+
setIntentStatus("readyToSearch")
41+
}
42+
}
3543

3644
if (loading) return <SearchSpinnerOverlay />
3745

@@ -45,7 +53,6 @@ export const AttributeLayer = ({
4553
<AttributeSelectorLayer
4654
isDisabled={disabled}
4755
aggregations={aggregations}
48-
filteredAggregations={filteredAggregations}
4956
config={config}
5057
/>
5158
</div>

vite_project/src/components/IntentDiscovery/IntentDiscoveryLayout.tsx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,10 @@ import {IntentDiscoveryOptions} from "./IntentDiscoveryOptions.tsx";
88
import {ProductRecommendations} from "./ProductRecommendations.tsx";
99
import type {MagentoProducts} from "../../hooks/infra/useProductAttributeLayer.tsx";
1010
import {useIntentSearch} from "../../hooks/domain/useIntentSearch.tsx";
11-
import {useSystemState} from "../../state/System/useSystemState.ts";
1211
import {SpinnerOverlay} from "../global/SpinnerOverlay.tsx";
1312
import {ErrorState} from "../global/ErrorState.tsx";
1413
import {useFindFilteredAttributeLayer} from "../../hooks/domain/useFindFilteredAttributeLayer.tsx";
14+
import {useIntentState} from "../../state/Intent/useIntentState.ts";
1515

1616
export interface Props {
1717
config: IntentDiscoveryDataConfig
@@ -37,7 +37,7 @@ export const IntentDiscoveryLayout = ({ config, categoryData, attributeLayerData
3737
config
3838
)
3939

40-
const { intentState } = useSystemState()
40+
const { intentState } = useIntentState()
4141

4242
if (attributeFilteredLayerLoading) return <SpinnerOverlay />
4343
if (attributeFilteredLayerError) return <ErrorState error={attributeFilteredLayerError} />
@@ -53,7 +53,6 @@ export const IntentDiscoveryLayout = ({ config, categoryData, attributeLayerData
5353
intent={intent}
5454
searchPossible={searchPossible}
5555
aggregations={attributeLayerData?.aggregations}
56-
filteredAggregations={attributeFilteredLayerData?.aggregations}
5756
disabled={isSearching}
5857
/>
5958
<IntentDiscoveryOptions

vite_project/src/components/SelectionsSummary/selectedPreferencesUtils.ts

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,25 +21,23 @@ export function isAttributeSelected(
2121
return false;
2222
}
2323

24-
export function renderPreferenceValue(
24+
export function getSelectedValues(
2525
attributeCode: string,
2626
aggregations: MagentoAggregation[],
2727
intent?: IntentRecord
28-
): string {
28+
): string[] {
2929
const scores = intent?.attributeScore?.[attributeCode];
30-
if (!scores) return "";
30+
if (!scores) return [];
3131

3232
const aggregation = aggregations.find(a => a.attribute_code === attributeCode);
3333

3434
return Object.entries(scores)
35-
.map(([val, score]) => {
35+
.map(([val]) => {
3636
const option = aggregation?.options.find(o => String(o.value) === String(val));
3737
const label = option?.label ?? val;
38-
const count = option?.count ?? score;
3938

40-
return `${label} (${count})`;
39+
return label;
4140
})
42-
.join(", ");
4341
}
4442

4543
export function getSelectedAttributes(
@@ -76,8 +74,8 @@ export function useSelectedPreferences(
7674
return valid ? String(valid.value) : null;
7775
};
7876

79-
const displayFor = (code: string): string => {
80-
return renderPreferenceValue(code, aggregations, intent);
77+
const displayFor = (code: string): string[] => {
78+
return getSelectedValues(code, aggregations, intent);
8179
};
8280

8381
return { selected, valueFor, displayFor };

vite_project/src/components/Suggestions/SuggestionCard.tsx

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@ import {formatPrice} from "../../lib/price.ts";
22
import type {EnrichedSuggestion} from "../../types/infra/magento/product.types.ts";
33

44
export const SuggestionCard: React.FC<{ suggestion: EnrichedSuggestion }> = ({ suggestion }) => {
5-
console.log('SuggestionCard', suggestion)
6-
75
const content = (
86
<>
97
<a

0 commit comments

Comments
 (0)