import type { ReactNode } from "react";
import type { PageProps, ParallelModalSearchParams } from "@/core/types/common";
import getStores from "@/app/_actions/getStores";
import getProductList from "@/app/(visualization)/project/@modals/(.)customization/_actions/getProductList";
import getProductFilters from "@/app/(visualization)/project/@modals/(.)customization/_actions/getProductFilters";
import { getServerSession } from "next-auth";
import { getFavourites } from "@/app/(visualization)/project/@modals/(.)customization/_actions/favourites";
import { authOptions } from "@/core/lib/auth/options";
import Logger from "@/core/utils/helpers/logger";
import ParallelModalWrapper from "@/core/components/ParallelModal";
import CustomizationComponent from "@/app/(visualization)/project/@modals/(.)customization/_components/Customization";
type Params = { segments: string[] };
/**
* This is separated _**server page**_ component which placed in the parallel slot `@modals`.
* The {@link CustomizationComponent} has a large infrastructure, so this way we separate it as completed _logical_ part.
* Such as this the server **page**, we're able to fetch required initial data on the server side and then put it to the children client components.
* The fetched data could be **revalidated** by some conditions as well if needed, with revalidation by tags – {@link revalidateTag}.
* @param props {@link PageParams
} props of the server page. In the current case, it is a **_parallel route_** slot `modals`.
* @returns {ReactNode} – client component node which got from _server-side_ fetched data as its `props`.
* @constructor
*/
export default async function CustomizationModal(
props: PageProps>): Promise {
Logger.dev("CustomizationModal.props.searchParams: ", props.searchParams);
const session = await getServerSession(authOptions);
Logger.dev("[CustomizationModal.session]: ", session);
/** Route segments which defines the selected categories & subcategories of product lists */
const category: string = props.params.segments[0].replace("textures", "texture"); // TODO
const subcategory: string | undefined = props.params.segments[1];
const subcategoryType: string | undefined = props.searchParams?.subcategoryType;
const savedProducts: boolean = (props.searchParams?.["saved"] !== undefined);
const shopIds = props.searchParams["shopIds"] as string | string[];
const shops: string[] = shopIds
? typeof shopIds === "string"
? props.searchParams["shopIds"]?.split(",")
: shopIds as string[]
: [];
Logger.log("SELECTED SHOPS: ", shops);
/** Markers for conditional fetching data */
const isPatternTiles: boolean = props.params.segments.includes("tile_patterns");
const isGrout: boolean = props.params.segments.includes("grout");
/** Request filter search params */
const requestFilterParams = new URLSearchParams();
/** Object to combine same params keys */
const combinedParams: Record = {};
/** Combine params .forEach callback */
const combineParams = (key: string) => {
const filterName = key.replace(/^(bar|common)/, '').toLowerCase();
if (combinedParams.hasOwnProperty(filterName)) {
combinedParams[filterName] += `,${props.searchParams[key]}`;
} else {
combinedParams[filterName] = props.searchParams[key];
}
}
/** Collecting common filter options below the product lists */
Object
.keys(props.searchParams)
.filter(o => o !== "modal" && o !== "origin" && o.startsWith("common"))
.forEach(combineParams);
/** Collecting barFilter options if barFilter has been applied */
if (props.searchParams?.["barFilter"] === "applied") {
Object
.keys(props.searchParams)
.filter(o => o !== "modal" && o !== "origin" && o !== "barFilter" && o.startsWith("bar"))
.forEach(combineParams);
}
/** Collecting unique param values in alphabetical order to correct caching the results */
const paramsEntries = Object.entries(combinedParams)
.sort(([keyA], [keyB]) => keyA.localeCompare(keyB))
.map(([key, value]) => [key, value.split(',').sort().join(',')]);
if (paramsEntries.length) {
for (const [key, value] of paramsEntries) {
const uniqueValues = Array.from(new Set(value.split(','))).join(',');
requestFilterParams.append(key.toLowerCase(), uniqueValues);
}
}
/** Development logging actual request params */
Logger.dev("Products request search params: ", requestFilterParams)
/**
* Create cached requests with keyParts signatures. Request caching will depend on changes of these key signatures
* TODO: Also, the dynamic data such as gotten from cookies() OR headers() will not supported by Next's cache.
* TODO: So if needed, this data should be gotten outside the cached function and passed to them as arguments.
* */
//const getCachedStores = cache
// .unstable_cache(getStores, [], { revalidate: 360, tags: ["cached_stores"] });
//const getCachedFilters = cache
// .unstable_cache(getProductFilters, [props.searchParams?.shopIds?.toString()], { tags: ["cached_filters"] });
///** Cached Products */
//const getCachedProducts = cache.unstable_cache(
// getProductList,
// [requestFilterParams.toString(), isPatternTiles.toString(), isGrout.toString(), props.searchParams?.shopIds?.toString()],
// { tags: ["cached_products"] });
//const getCachedTiles = cache.unstable_cache(
// getProductList,
// [requestFilterParams.toString(), isPatternTiles.toString(), props.searchParams?.shopIds?.toString()],
// { tags: ["cached_tiles"] });
//const getCachedGrout = cache.unstable_cache(
// getProductList,
// [requestFilterParams.toString(), props.searchParams?.shopIds?.toString()],
// { tags: ["cached_grout"] });
/** Fetch stores data first */
const stores = await getStores(shops);
/** The concurrent conditional fetching data */
const [filters, products, grout, tiles, favourites] = await Promise.all([
shops ? getProductFilters(shops) : [],
(!isPatternTiles && !isGrout)
? getProductList(
category, subcategory, subcategoryType,
requestFilterParams, shops, undefined, savedProducts) : [],
getProductList("texture", "grout", undefined, undefined, shops),
isPatternTiles ? getProductList("texture", "floor_tile", undefined, undefined, shops) : undefined,
getFavourites(session?.user.id || "")
]);
/** Development logging */
Logger.dev("PROMISE.ALL - STORES: ", stores);
Logger.dev("PROMISE.ALL - FILTERS: ", filters);
Logger.dev("PROMISE.ALL - PRODUCTS.length: ", products?.length);
Logger.dev("PROMISE.ALL - FAVOURITES: ", favourites);
Logger.dev("PROMISE.ALL - GROUT.length: ", grout?.length);
Logger.dev("PROMISE.ALL - TILES.length: ", tiles?.length);
return (
);
}