437 lines
12 KiB
TypeScript
437 lines
12 KiB
TypeScript
// @ts-strict-ignore
|
|
import {
|
|
ChannelSaleFormData,
|
|
SaleDetailsPageFormData,
|
|
} from "@dashboard/discounts/components/SaleDetailsPage";
|
|
import { VoucherDetailsPageFormData } from "@dashboard/discounts/components/VoucherDetailsPage";
|
|
import { RequirementsPicker } from "@dashboard/discounts/types";
|
|
import {
|
|
ChannelDetailsFragment,
|
|
ChannelFragment,
|
|
CollectionDetailsFragment,
|
|
ProductDetailsVariantFragment,
|
|
ProductFragment,
|
|
ProductVariantDetailsQuery,
|
|
SaleDetailsFragment,
|
|
SaleType,
|
|
ShippingZoneQuery,
|
|
VoucherDetailsFragment,
|
|
} from "@dashboard/graphql";
|
|
import { RequireOnlyOne } from "@dashboard/misc";
|
|
import { validatePrice } from "@dashboard/products/utils/validation";
|
|
import { mapNodeToChoice } from "@dashboard/utils/maps";
|
|
import uniqBy from "lodash/uniqBy";
|
|
|
|
export interface Channel {
|
|
id: string;
|
|
name: string;
|
|
}
|
|
|
|
export interface ChannelData {
|
|
id: string;
|
|
name: string;
|
|
isPublished?: boolean;
|
|
publicationDate?: string | null;
|
|
currency?: string;
|
|
variantsIds?: string[];
|
|
price?: string;
|
|
costPrice?: string;
|
|
availableForPurchase?: string;
|
|
isAvailableForPurchase?: boolean;
|
|
visibleInListings?: boolean;
|
|
preorderThreshold?: number;
|
|
unitsSold?: number;
|
|
}
|
|
|
|
export interface ProductChannelListingData extends Channel {
|
|
isPublished: boolean;
|
|
publicationDate: string | null;
|
|
availableForPurchase: string;
|
|
isAvailableForPurchase: boolean;
|
|
visibleInListings: boolean;
|
|
currency?: string;
|
|
}
|
|
|
|
export interface ChannelPriceData {
|
|
id: string;
|
|
name: string;
|
|
currency: string;
|
|
price: string;
|
|
costPrice?: string;
|
|
}
|
|
|
|
export interface IChannelPriceArgs {
|
|
price: string;
|
|
costPrice: string;
|
|
}
|
|
export type ChannelPriceArgs = RequireOnlyOne<
|
|
IChannelPriceArgs,
|
|
"price" | "costPrice"
|
|
>;
|
|
|
|
export interface ChannelPreorderArgs {
|
|
preorderThreshold: number;
|
|
unitsSold: number;
|
|
hasPreorderEndDate: boolean;
|
|
preorderEndDateTime?: string;
|
|
}
|
|
|
|
export interface ChannelPriceAndPreorderData {
|
|
id: string;
|
|
name: string;
|
|
currency: string;
|
|
price: string;
|
|
costPrice?: string;
|
|
preorderThreshold?: number | null;
|
|
unitsSold?: number;
|
|
}
|
|
|
|
export interface IChannelPriceAndPreorderArgs {
|
|
price: string;
|
|
costPrice: string;
|
|
preorderThreshold?: number | null;
|
|
unitsSold?: number;
|
|
}
|
|
export type ChannelPriceAndPreorderArgs = IChannelPriceArgs & {
|
|
preorderThreshold: number | null;
|
|
unitsSold?: number;
|
|
};
|
|
|
|
export interface ChannelVoucherData {
|
|
id: string;
|
|
name: string;
|
|
discountValue: string;
|
|
currency: string;
|
|
minSpent: string;
|
|
}
|
|
|
|
export interface ChannelSaleData {
|
|
id: string;
|
|
name: string;
|
|
discountValue: string;
|
|
currency: string;
|
|
}
|
|
|
|
export interface ChannelCollectionData {
|
|
id: string;
|
|
isPublished: boolean;
|
|
name: string;
|
|
publicationDate: string | null;
|
|
}
|
|
|
|
export const createCollectionChannels = (data?: ChannelFragment[]) =>
|
|
data?.map(channel => ({
|
|
id: channel.id,
|
|
isPublished: false,
|
|
name: channel.name,
|
|
publicationDate: null,
|
|
}));
|
|
|
|
export const createVoucherChannels = (data?: ChannelFragment[]) =>
|
|
data?.map(channel => ({
|
|
currency: channel.currencyCode,
|
|
discountValue: "",
|
|
id: channel.id,
|
|
minSpent: "",
|
|
name: channel.name,
|
|
}));
|
|
|
|
export const createSaleChannels = (data?: ChannelFragment[]) =>
|
|
data?.map(channel => ({
|
|
currency: channel.currencyCode,
|
|
discountValue: "",
|
|
id: channel.id,
|
|
name: channel.name,
|
|
percentageValue: "",
|
|
fixedValue: "",
|
|
}));
|
|
|
|
export const createVariantChannels = (
|
|
data?: ProductVariantDetailsQuery["productVariant"],
|
|
): ChannelPriceData[] => {
|
|
if (data) {
|
|
return data?.channelListings.map(listing => ({
|
|
costPrice: listing.costPrice?.amount.toString() || "",
|
|
currency: listing.channel.currencyCode,
|
|
id: listing.channel.id,
|
|
name: listing.channel.name,
|
|
price: listing.price?.amount?.toString(),
|
|
}));
|
|
}
|
|
return [];
|
|
};
|
|
|
|
export const createChannelsDataWithSaleDiscountPrice = (
|
|
saleData?: SaleDetailsFragment,
|
|
data?: ChannelFragment[],
|
|
): ChannelSaleData[] => {
|
|
if (data && saleData?.channelListings) {
|
|
const dataArr = createSaleChannels(data);
|
|
|
|
const saleDataArr = createChannelsDataFromSale(saleData);
|
|
return uniqBy([...saleDataArr, ...dataArr], obj => obj.id);
|
|
}
|
|
return [];
|
|
};
|
|
|
|
export const createChannelsDataWithDiscountPrice = (
|
|
voucherData?: VoucherDetailsFragment,
|
|
data?: ChannelFragment[],
|
|
): ChannelVoucherData[] => {
|
|
if (data && voucherData?.channelListings) {
|
|
const dataArr = createVoucherChannels(data);
|
|
|
|
const voucherDataArr = createChannelsDataFromVoucher(voucherData);
|
|
return uniqBy([...voucherDataArr, ...dataArr], obj => obj.id);
|
|
}
|
|
return [];
|
|
};
|
|
|
|
export const createChannelsData = (data?: ChannelFragment[]): ChannelData[] =>
|
|
data?.map(channel => ({
|
|
availableForPurchase: null,
|
|
costPrice: "",
|
|
currency: channel.currencyCode,
|
|
id: channel.id,
|
|
isAvailableForPurchase: true,
|
|
variantsIds: [],
|
|
isPublished: true,
|
|
name: channel.name,
|
|
price: "",
|
|
publicationDate: null,
|
|
visibleInListings: true,
|
|
})) || [];
|
|
|
|
export const createChannelsDataWithPrice = (
|
|
productData?: ProductFragment,
|
|
data?: ChannelFragment[],
|
|
): ChannelData[] => {
|
|
if (data && productData?.channelListings) {
|
|
const dataArr = createChannelsData(data);
|
|
const productDataArr = createChannelsDataFromProduct(productData);
|
|
|
|
return uniqBy([...productDataArr, ...dataArr], obj => obj.id);
|
|
}
|
|
return [];
|
|
};
|
|
|
|
export const createShippingChannels = (
|
|
data?: ShippingZoneQuery["shippingZone"]["channels"],
|
|
): ChannelShippingData[] =>
|
|
data?.map(channel => ({
|
|
currency: channel.currencyCode,
|
|
id: channel.id,
|
|
maxValue: "",
|
|
minValue: "",
|
|
name: channel.name,
|
|
price: "",
|
|
})) || [];
|
|
|
|
export const createShippingChannelsFromRate = (
|
|
data?: ShippingZoneQuery["shippingZone"]["shippingMethods"][0]["channelListings"],
|
|
): ChannelShippingData[] =>
|
|
data?.map(channelData => ({
|
|
currency: channelData.channel.currencyCode,
|
|
id: channelData.channel.id,
|
|
maxValue: channelData.maximumOrderPrice
|
|
? channelData.maximumOrderPrice.amount.toString()
|
|
: "",
|
|
minValue: channelData.minimumOrderPrice
|
|
? channelData.minimumOrderPrice.amount.toString()
|
|
: "",
|
|
name: channelData.channel.name,
|
|
price: channelData.price ? channelData.price.amount.toString() : "",
|
|
})) || [];
|
|
|
|
export const createCollectionChannelsData = (
|
|
collectionData?: CollectionDetailsFragment,
|
|
) => {
|
|
if (collectionData?.channelListings) {
|
|
const collectionDataArr = collectionData?.channelListings.map(listing => ({
|
|
id: listing.channel.id,
|
|
isPublished: listing.isPublished,
|
|
name: listing.channel.name,
|
|
publicationDate: listing.publicationDate,
|
|
}));
|
|
return collectionDataArr;
|
|
}
|
|
};
|
|
|
|
export interface ChannelShippingData {
|
|
currency: string;
|
|
id: string;
|
|
minValue: string;
|
|
name: string;
|
|
maxValue: string;
|
|
price: string;
|
|
}
|
|
|
|
export const createChannelsDataFromVoucher = (
|
|
voucherData?: VoucherDetailsFragment,
|
|
) =>
|
|
voucherData?.channelListings?.map(option => ({
|
|
currency: option.channel.currencyCode || option?.minSpent?.currency || "",
|
|
discountValue: option.discountValue.toString() || "",
|
|
id: option.channel.id,
|
|
minSpent: option?.minSpent?.amount.toString() || "",
|
|
name: option.channel.name,
|
|
})) || [];
|
|
|
|
export const createChannelsDataFromSale = (
|
|
saleData?: SaleDetailsFragment,
|
|
): ChannelSaleFormData[] =>
|
|
saleData?.channelListings?.map(option => ({
|
|
currency: option.channel.currencyCode || "",
|
|
discountValue: option.discountValue.toString() || "",
|
|
id: option.channel.id,
|
|
name: option.channel.name,
|
|
percentageValue:
|
|
saleData.type === SaleType.PERCENTAGE
|
|
? option.discountValue.toString()
|
|
: "",
|
|
fixedValue:
|
|
saleData.type === SaleType.FIXED ? option.discountValue.toString() : "",
|
|
})) || [];
|
|
|
|
export const createChannelsDataFromProduct = (productData?: ProductFragment) =>
|
|
productData?.channelListings?.map(
|
|
({
|
|
channel,
|
|
availableForPurchase,
|
|
isAvailableForPurchase,
|
|
visibleInListings,
|
|
publicationDate,
|
|
isPublished,
|
|
}) => {
|
|
const variantChannel = productData.variants[0]?.channelListings.find(
|
|
listing => listing.channel.id === channel.id,
|
|
);
|
|
// Comparing explicitly to false because `hasVariants` can be undefined
|
|
const isSimpleProduct = !productData.productType?.hasVariants;
|
|
const haveVariantsChannelListings = productData.variants.some(variant =>
|
|
variant.channelListings.some(
|
|
listing => listing.channel.id === channel.id,
|
|
),
|
|
);
|
|
const price = variantChannel?.price;
|
|
const costPrice = variantChannel?.costPrice;
|
|
const variantsIds = extractVariantsIdsForChannel(
|
|
productData.variants,
|
|
channel.id,
|
|
);
|
|
const soldUnits = variantChannel?.preorderThreshold?.soldUnits;
|
|
const preorderThreshold = variantChannel?.preorderThreshold?.quantity;
|
|
// Published defaults to true if none of variants have set channel listing yet
|
|
const isProductPublished =
|
|
!isSimpleProduct && !haveVariantsChannelListings ? true : isPublished;
|
|
|
|
return {
|
|
availableForPurchase,
|
|
isPublished: isProductPublished,
|
|
publicationDate,
|
|
variantsIds,
|
|
costPrice: costPrice?.amount.toString() ?? "",
|
|
currency: price ? price.currency : "",
|
|
id: channel.id,
|
|
isAvailableForPurchase: !!isAvailableForPurchase,
|
|
name: channel.name,
|
|
price: price ? price.amount.toString() : "",
|
|
visibleInListings: !!visibleInListings,
|
|
soldUnits,
|
|
preorderThreshold,
|
|
};
|
|
},
|
|
) || [];
|
|
|
|
export const extractVariantsIdsForChannel = (
|
|
productVariants: ProductDetailsVariantFragment[],
|
|
channelId: string,
|
|
) =>
|
|
productVariants
|
|
?.filter(({ channelListings }) =>
|
|
channelListings.some(({ channel }) => channel.id === channelId),
|
|
)
|
|
.map(({ id }) => id) || [];
|
|
|
|
export const createSortedChannelsDataFromProduct = (
|
|
productData?: ProductFragment,
|
|
): ChannelData[] =>
|
|
createChannelsDataFromProduct(productData).sort((channel, nextChannel) =>
|
|
channel.name.localeCompare(nextChannel.name),
|
|
);
|
|
|
|
export const createSortedChannelsData = (data?: ChannelFragment[]) =>
|
|
createChannelsData(data)?.sort((channel, nextChannel) =>
|
|
channel.name.localeCompare(nextChannel.name),
|
|
);
|
|
|
|
export const createSortedShippingChannels = (
|
|
data?: ShippingZoneQuery["shippingZone"]["channels"],
|
|
) =>
|
|
createShippingChannels(data)?.sort((channel, nextChannel) =>
|
|
channel.name.localeCompare(nextChannel.name),
|
|
);
|
|
|
|
export const createSortedShippingChannelsFromRate = (
|
|
data?: ShippingZoneQuery["shippingZone"]["shippingMethods"][0]["channelListings"],
|
|
) =>
|
|
createShippingChannelsFromRate(data)?.sort((channel, nextChannel) =>
|
|
channel.name.localeCompare(nextChannel.name),
|
|
);
|
|
|
|
export const createSortedVoucherData = (data?: ChannelFragment[]) =>
|
|
createVoucherChannels(data)?.sort((channel, nextChannel) =>
|
|
channel.name.localeCompare(nextChannel.name),
|
|
);
|
|
|
|
export const createSortedSaleData = (data?: ChannelFragment[]) =>
|
|
createSaleChannels(data)?.sort((channel, nextChannel) =>
|
|
channel.name.localeCompare(nextChannel.name),
|
|
);
|
|
|
|
export const createSortedChannelsDataFromVoucher = (
|
|
data?: VoucherDetailsFragment,
|
|
) =>
|
|
createChannelsDataFromVoucher(data)?.sort((channel, nextChannel) =>
|
|
channel.name.localeCompare(nextChannel.name),
|
|
);
|
|
|
|
export const createSortedChannelsDataFromSale = (
|
|
data?: SaleDetailsFragment,
|
|
): ChannelSaleFormData[] =>
|
|
createChannelsDataFromSale(data)?.sort((channel, nextChannel) =>
|
|
channel.name.localeCompare(nextChannel.name),
|
|
);
|
|
|
|
export const getChannelsCurrencyChoices = (
|
|
id: string,
|
|
selectedChannel: ChannelDetailsFragment,
|
|
channelsList: ChannelDetailsFragment[],
|
|
) =>
|
|
id
|
|
? mapNodeToChoice(
|
|
channelsList?.filter(
|
|
channel =>
|
|
channel.id !== id &&
|
|
channel.currencyCode === selectedChannel?.currencyCode,
|
|
),
|
|
)
|
|
: [];
|
|
|
|
export const validateSalePrice = (
|
|
data: SaleDetailsPageFormData,
|
|
channel: ChannelSaleFormData,
|
|
) =>
|
|
validatePrice(
|
|
data.type === SaleType.PERCENTAGE
|
|
? channel.percentageValue
|
|
: channel.fixedValue,
|
|
);
|
|
|
|
export const validateVoucherPrice = (
|
|
data: VoucherDetailsPageFormData,
|
|
channel: ChannelVoucherData,
|
|
) =>
|
|
validatePrice(channel.discountValue) ||
|
|
(data.requirementsPicker === RequirementsPicker.ORDER &&
|
|
validatePrice(channel.minSpent));
|