Skip to content

Commit b8a47d3

Browse files
Merge pull request #5 from reactedge/feat/intent-state-refactor
Refactor Intent Discovery state model and stabilize UX flow
2 parents 07b935c + 04659fa commit b8a47d3

61 files changed

Lines changed: 415 additions & 681 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

vite_project/index.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
<script type="module" src="./src/widget.ts"></script>
77

88
<script>
9-
window.ReactEdgeIntent = window.ReactEdgeIntent || {
9+
/*window.ReactEdgeIntent = window.ReactEdgeIntent || {
1010
emit: (signal) => {
1111
window.dispatchEvent(
1212
new CustomEvent("reactedge:intent", { detail: signal })
@@ -22,7 +22,7 @@
2222
}
2323
})
2424
);
25-
}
25+
}*/
2626
</script>
2727

2828
<div style="padding:20px; border:1px solid #ccc; margin-top:20px;">

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.6.0",
4+
"version": "0.6.1",
55
"type": "module",
66
"scripts": {
77
"dev": "vite",

vite_project/src/IntentDiscoveryWidgetWrapper.tsx

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,18 @@ import {ErrorState} from "./components/global/ErrorState.tsx";
33
import {SystemStateProvider} from "./state/System/SystemStateProvider.tsx";
44
import {IntentLookup} from "./components/IntentLookup.tsx";
55
import {TranslationStateProvider} from "./state/Translation/TranslationStateProvider.tsx";
6+
import {SpinnerOverlay} from "./components/global/SpinnerOverlay.tsx";
67

78
type Props = {
89
host: HTMLElement;
910
};
1011

11-
function SearchOverlay() {
12-
return null;
13-
}
14-
1512
export const IntentDiscoveryWidgetWrapper = ({ host }: Props) => {
1613
const {config, error, loading} = useWidgetConfig(host);
1714

1815
if (!config) return null;
1916
if (error) return <ErrorState error={error} />
20-
if (loading) return <SearchOverlay />
17+
if (loading) return <SpinnerOverlay />
2118

2219
return <SystemStateProvider config={config.integrations} store={config.storeCode}>
2320
<TranslationStateProvider translations={config.translations}>

vite_project/src/components/AttributeLayer/AttributeSelectorLayer.tsx

Lines changed: 20 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
1-
import {useActiveAttributeState} from "../../state/ActiveAttribute/useActiveAttributeState.ts";
1+
import { useSelectedPreferences } from "../SelectionsSummary/selectedPreferencesUtils.ts";
2+
import { useState } from "react";
3+
import { useIntentAttributes } from "../../hooks/domain/useIntentAttributes.tsx";
4+
import { AttributeTile } from "./AttributeTile.tsx";
5+
import { useTranslationState } from "../../state/Translation/useTranslationState.ts";
6+
import type { IntentDiscoveryDataConfig } from "../../domain/intent-discovery.types.ts";
7+
import type { MagentoAggregation } from "../../hooks/infra/useProductAttributeLayer.tsx";
8+
import { useInteractionState } from "../../state/Interaction/useInteractionState.ts";
29
import {useSystemState} from "../../state/System/useSystemState.ts";
3-
import {useSelectedPreferences} from "../SelectionsSummary/selectedPreferencesUtils.ts";
4-
import {useState} from "react";
5-
import {useIntentAttributes} from "../../hooks/domain/useIntentAttributes.tsx";
6-
import {AttributeTile} from "./AttributeTile.tsx";
7-
import {useTranslationState} from "../../state/Translation/useTranslationState.ts";
8-
import type {IntentDiscoveryDataConfig} from "../../domain/intent-discovery.types.ts";
9-
import type {MagentoAggregation} from "../../hooks/infra/useProductAttributeLayer.tsx";
10+
import {NoResult} from "../global/NoResult.tsx";
1011

1112
type Props = {
1213
isDisabled: boolean
@@ -15,25 +16,25 @@ type Props = {
1516
}
1617

1718
export const AttributeSelectorLayer = ({
18-
isDisabled,
19-
aggregations,
20-
config
21-
}: Props) => {
22-
const { intentState } = useSystemState()
23-
const { setActiveAttributeCode } = useActiveAttributeState();
19+
isDisabled,
20+
aggregations,
21+
config
22+
}: Props) => {
23+
const { setActiveAttribute } = useInteractionState();
2424
const [showAll, setShowAll] = useState(false);
25+
const {intentState} = useSystemState()
2526

2627
const allAttributes = useIntentAttributes(aggregations, config)
2728
const visibleAttributes = showAll ? allAttributes : allAttributes.slice(0, 3);
28-
const { valueFor: prefValue } =
29+
const { displayFor } =
2930
useSelectedPreferences(aggregations, intentState);
30-
const {t} = useTranslationState()
31+
const { t } = useTranslationState()
3132

3233
const isAttributeSelected = (code: string) =>
3334
code in (intentState?.attributeScore ?? {}) ||
3435
(code === 'price' && Object.keys(intentState?.priceAffinity ?? {}).length > 0);
3536

36-
if (!aggregations?.length) return null;
37+
if (!aggregations?.length) return <NoResult />;
3738

3839
return (
3940
<div className={`step-finder ${isDisabled ? 'step-finder--disabled' : ''}`}>
@@ -46,8 +47,8 @@ export const AttributeSelectorLayer = ({
4647
key={code}
4748
attr={attr}
4849
isSelected={isSelected}
49-
value={prefValue(code)}
50-
onClick={() => setActiveAttributeCode(code)}
50+
value={displayFor(code)}
51+
onClick={() => setActiveAttribute(code)}
5152
/>
5253
);
5354
})}

vite_project/src/components/AttributeLayer/IntentExplanation.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,6 @@ export const IntentExplanation = ({
1515

1616
return (
1717
<div className="intent-explanations">
18-
<label className="intent-subtitle">
19-
{t("Describe what you're looking for")}
20-
</label>
21-
2218
<button
2319
onClick={onAsk}
2420
className="filter-apply-button"
@@ -32,6 +28,10 @@ export const IntentExplanation = ({
3228
? t("Add %s+ characters in the text intent or refine your preferences", remainingChars)
3329
: t("AI ready to interpret your request")}
3430
</div>
31+
32+
<label className="intent-subtitle">
33+
{t("Describe what you're looking for")}
34+
</label>
3535
</div>
3636
);
3737
};

vite_project/src/components/FinderWidget/ResultMatch.tsx

Lines changed: 0 additions & 39 deletions
This file was deleted.

vite_project/src/components/FinderWidget/StepFinder.tsx

Lines changed: 15 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,31 @@
11
import { unescapeHtml } from "../../lib/string.ts";
22
import { useFindAttributeOptionsByCode } from "../../hooks/domain/useFindAttributeOptionsByCode.tsx";
3-
import { useOptionPreferenceState } from "../../state/OptionPreference/useOptionPreferenceState.ts";
4-
import { useActiveAttributeState } from "../../state/ActiveAttribute/useActiveAttributeState.ts";
53
import type {MagentoAggregationOption, MagentoProducts} from "../../hooks/infra/useProductAttributeLayer.tsx";
4+
import {useSystemState} from "../../state/System/useSystemState.ts";
5+
import {useInteractionState} from "../../state/Interaction/useInteractionState.ts";
66
import {activity} from "../../activity";
77

8-
98
interface StepFinderProps {
109
optionCode: string
1110
attributeLayerData: MagentoProducts
1211
}
1312

1413
export const StepFinder: React.FC<StepFinderProps> = ({ optionCode, attributeLayerData }: StepFinderProps) => {
15-
const { optionState, toggleOptionSelection } = useOptionPreferenceState()
16-
const { setActiveAttributeCode } = useActiveAttributeState()
14+
const { setActiveAttribute, setFocusedOption } = useInteractionState()
1715
const { attributeData } = useFindAttributeOptionsByCode(optionCode, attributeLayerData)
16+
const { setPreference } = useSystemState()
17+
const {intentState} = useSystemState()
1818

19-
// find the currently selected value for this option code
20-
const currentSelection = optionState.optionSelection.find(sel => sel.code === optionCode);
21-
22-
const onChange = async (option: MagentoAggregationOption) => {
23-
setActiveAttributeCode(optionCode);
24-
const action = toggleOptionSelection(optionCode, attributeData.label, option.value, option.label);
19+
const handleOnClick = async (option: MagentoAggregationOption) => {
20+
setActiveAttribute(optionCode);
21+
setPreference(optionCode, option.value)
22+
setFocusedOption(option.value)
2523

26-
activity('select-options', `Select ${optionCode}`, action);
27-
28-
if (action === 'select') {
29-
window.ReactEdgeIntent.emit({
30-
type: 'filter_select',
31-
attribute: optionCode,
32-
value: option.value
33-
});
34-
} else {
35-
window.ReactEdgeIntent.emit({
36-
type: 'filter_deselect',
37-
attribute: optionCode,
38-
value: option.value
39-
});
40-
}
24+
activity('intent-discovery-option', 'Intent Option Selection', {intentState, optionCode, value: option.value});
4125
};
4226

43-
// check if current option value is selected (handles both single and multiple selections)
44-
const isOptionSelected = (optionValue: string): boolean => {
45-
if (!currentSelection) return false;
46-
return currentSelection.value === optionValue;
47-
};
27+
const selectedMap = intentState.attributeScore?.[optionCode] || {};
28+
const isOptionSelected = (value: string) => value in selectedMap;
4829

4930
return (
5031
<div className="step-finder">
@@ -59,8 +40,10 @@ export const StepFinder: React.FC<StepFinderProps> = ({ optionCode, attributeLay
5940
<input
6041
type="radio"
6142
name="preference"
43+
checked={isOptionSelected(option.value)}
6244
value={option.value}
63-
onChange={() => onChange(option)}
45+
onClick={() => handleOnClick(option)}
46+
readOnly
6447
/>
6548

6649
<span className={`choice-tile__label ${isOptionSelected(option.value) ? 'choice-tile__label--active' : ''}`}>

vite_project/src/components/FinderWidget/StepPriceFinder.tsx

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import {useOptionPreferenceState} from "../../state/OptionPreference/useOptionPreferenceState.ts";
21
import {useFindAttributeOptionsByCode} from "../../hooks/domain/useFindAttributeOptionsByCode.tsx";
32
import {formatRange} from "../../lib/price.ts";
43
import type {MagentoAggregationOption, MagentoProducts} from "../../hooks/infra/useProductAttributeLayer.tsx";
@@ -9,14 +8,10 @@ interface StepFinderProps {
98

109
export const StepPriceFinder = ({attributeLayerData}: StepFinderProps) => {
1110
const option = 'price'
12-
const {setOptionSelection, setActiveOptionCode} = useOptionPreferenceState()
1311
const {attributeData} = useFindAttributeOptionsByCode(option, attributeLayerData)
1412

15-
const onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
16-
const input = e.target;
17-
18-
setActiveOptionCode('result');
19-
setOptionSelection(option, attributeData.label, input.value, input.value);
13+
const onChange = () => {
14+
//
2015
};
2116

2217
return (

vite_project/src/components/IntentDiscovery/AttributeLayer.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@ import {useSystemState} from "../../state/System/useSystemState.ts";
44
import type {MagentoAggregation} from "../../hooks/infra/useProductAttributeLayer.tsx";
55
import type {IntentControllerState} from "../../domain/intent.types.ts";
66
import {useOptionLabelMap} from "../../hooks/domain/useOptionLabelMap.ts";
7-
import {SpinnerOverlay} from "../SpinnerOverlay.tsx";
87
import {useAskAi} from "../../hooks/domain/useAiInterpretation.tsx";
98
import {AttributeSelectorLayer} from "../AttributeLayer/AttributeSelectorLayer.tsx";
109
import {IntentExplanation} from "../AttributeLayer/IntentExplanation.tsx";
10+
import {SearchSpinnerOverlay} from "../global/SearchSpinnerOverlay.tsx";
1111

1212
type Props = {
1313
config: IntentDiscoveryDataConfig
@@ -24,9 +24,10 @@ export const AttributeLayer = ({
2424
aggregations,
2525
disabled
2626
}: Props) => {
27-
const { intentState, setIntentText, setIntentStatus, setPreference, intentApiClient } = useSystemState()
27+
const { setIntentText, setIntentStatus, setPreference, intentEngine, intentState } = useSystemState()
2828
const optionLabelMap = useOptionLabelMap(aggregations);
2929
const [loading, setLoading] = useState(false);
30+
const intentApiClient = intentEngine.getApiClient()
3031

3132
const handleAsk = useAskAi({
3233
intentState,
@@ -41,7 +42,7 @@ export const AttributeLayer = ({
4142
setLoading
4243
});
4344

44-
if (loading) return <SpinnerOverlay />
45+
if (loading) return <SearchSpinnerOverlay />
4546

4647
return (
4748
<div className="finder">

0 commit comments

Comments
 (0)