@heyframe/composables
Version:
HeyFrame Frontends composables for Vue
136 lines (123 loc) • 3.69 kB
text/typescript
import { getTranslatedProperty } from "@heyframe/helpers";
import { computed, ref } from "vue";
import type { ComputedRef, Ref } from "vue";
import { useProduct, useHeyFrameContext } from "#imports";
import type { Schemas } from "#heyframe";
export type UseProductConfiguratorReturn = {
/**
* Handler for action when the selected option is changed
*/
handleChange(
attribute: string,
option: string,
onChangeHandled: () => void,
): Promise<void>;
findVariantForSelectedOptions(options?: {
[key: string]: string;
}): Promise<Schemas["Product"] | undefined>;
/**
* Indicates if the options are being (re)loaded
*/
isLoadingOptions: Ref<boolean>;
/**
* Object of currently selected options
*/
getSelectedOptions: ComputedRef<{
[key: string]: string;
}>;
/**
* All assigned properties which the variant can be made of
*/
getOptionGroups: ComputedRef<Schemas["PropertyGroup"][]>;
};
/**
* Composable to change product variant.
* @public
* @category Product
*/
export function useProductConfigurator(): UseProductConfiguratorReturn {
const { apiClient } = useHeyFrameContext();
const { configurator, product } = useProduct();
const selected = ref<{
[key: string]: string;
}>({});
const isLoadingOptions = ref(!!product.value.options?.length);
const parentProductId = computed(() => product.value?.parentId);
const getOptionGroups = computed<Schemas["PropertyGroup"][]>(() => {
return configurator.value || [];
});
const findGroupCodeForOption = (optionId: string) => {
const group = getOptionGroups.value.find((optionGroup) => {
const optionFound = optionGroup.options?.find((option) => {
return option.id === optionId;
});
return !!optionFound;
});
return getTranslatedProperty(group, "name");
};
// create a group -> optionId map
for (const optionId of product.value.optionIds || []) {
const optionGroupCode = findGroupCodeForOption(optionId);
if (optionGroupCode) {
selected.value[optionGroupCode] = optionId;
}
}
async function findVariantForSelectedOptions(options?: {
[code: string]: string;
}): Promise<Schemas["Product"] | undefined> {
const filter: Schemas["Filters"] = [
{
type: "equals",
field: "parentId",
value: parentProductId.value as string,
},
...Object.values(options || selected.value).map(
(id) =>
({
type: "equals",
field: "optionIds",
value: id as string,
}) as Schemas["EqualsFilter"],
),
];
try {
const response = await apiClient.invoke("readProduct post /product", {
body: {
filter,
limit: 1,
includes: {
product: ["id", "translated", "productNumber", "seoUrls"],
seo_url: ["seoPathInfo"],
},
associations: {
seoUrls: {},
},
},
});
return response.data.elements?.[0]; // return first matching product
} catch (e) {
console.error("SwProductDetails:findVariantForSelectedOptions", e);
return undefined;
}
}
const handleChange = async (
group: string,
option: string,
onChangeHandled?: () => void,
): Promise<void> => {
selected.value = Object.assign({}, selected.value, {
[group]: option,
});
if (typeof onChangeHandled === "function") {
// run passed callback
await onChangeHandled();
}
};
return {
handleChange,
findVariantForSelectedOptions,
isLoadingOptions,
getOptionGroups,
getSelectedOptions: computed(() => selected.value),
};
}