import { createDirectus, DirectusClient, readItems, rest, RestClient } from '@directus/sdk'; import { escapeRegExp, find, mapValues, omit, omitBy, pick, pickBy, throttle } from 'lodash-es'; import { FooterMediaItemProps, FooterMenuProps } from '../../components'; import { Level1Props } from '../../components/Header/types'; export type { Level1Props }; const defaultLocale = 'en'; const translateFields = (objs: Record[], keys: Array, locale: string) => { const t1 = find(objs, (t) => t.languages_code === defaultLocale); const t2 = find(objs, (t) => t.languages_code === locale); if (!t2) return pick(t1, keys); return pick( mapValues(t2, (v, k) => v || t1?.[k] || ''), keys, ); }; const translateLink = (link: string, locale: string) => { if (!link) return link; return link.replace(new RegExp(escapeRegExp('${locale}'), 'ig'), locale); }; type FooterMenuItemSchema = { href: string; data_analytics_id: string; target: '_self' | '_blank'; translations: Array<{ languages_code: string; title: string; }>; }; type FooterMediaItemSchema = { href: string; data_analytics_id: string; target: '_self' | '_blank'; icon?: any; name?: string; children: Array<{ name: string; icon?: any; href: string; target: '_self' | '_blank'; data_analytics_id: string; }>; }; type FooterMenuSchema = { id: number; translations: Array<{ languages_code: string; menu: string }>; items: Array; }; const transformFooterMenu = (data: FooterMenuSchema[], locale: string) => { return data.map((menu) => { const fields = translateFields(menu.translations, ['menu'], locale); const items = menu.items.map((i) => { const fields = translateFields(i.translations, ['title'], locale); return { ...omit(i, 'translations'), title: fields.title || '', href: translateLink(i.href, locale), target: i.target, 'data-analytics-id': i.data_analytics_id, }; }); return { ...omit(menu, 'translations'), menu: fields.menu || '', items }; }); }; let menus = Array(); let headerMenus = Array(); let media = Array(); const getFooterMenus = async (client: DirectusClient & RestClient, locale: string) => { const _getMenus = () => { // fetch from server if (typeof window !== 'undefined') return Promise.resolve([]); return client .request( // @ts-ignore ts v5 readItems('footer_nav', { fields: ['id', { translations: ['*'] }, { items: ['*', { translations: ['*'] }] }], }), ) .then((m) => (menus = m)); }; try { if (menus.length && media.length) { _getMenus(); return transformFooterMenu(menus, locale); } const result = await _getMenus(); return transformFooterMenu(result, locale); } catch { return []; } }; const transformFooterMedia = (data: FooterMediaItemSchema[], prefix: string) => { return data.map((media) => { const children = media.children.map((i) => { let child_icon; if (i?.icon?.filename_disk) { child_icon = `${prefix}/${i?.icon?.filename_disk}`; } return { ...i, icon: child_icon || undefined, 'data-analytics-id': i.data_analytics_id, }; }); let mediaIcon; if (media?.icon?.filename_disk) { mediaIcon = `${prefix}/${media?.icon?.filename_disk}`; } return { children, 'data-analytics-id': media.data_analytics_id, target: media.target, href: media.href, name: media.name, icon: mediaIcon || undefined, }; }); }; const getFooterMediaList = async ( client: DirectusClient & RestClient, prefix: string, ) => { const _getMedia = () => { // fetch from server if (typeof window !== 'undefined') return Promise.resolve([]); return client .request( // @ts-ignore ts v5 readItems('footer_media', { fields: ['*.*', { children: ['*.*'] }], }), ) .then((m) => (media = m)); }; try { if (media.length) { _getMedia(); return transformFooterMedia(media, prefix); } const result = await _getMedia(); return transformFooterMedia(result, prefix); } catch { return []; } }; const isObject = (obj: any) => { return Object.prototype.toString.call(obj) === '[object Object]'; }; const transformHeaderMenuObject = (obj: Record, locale: string, prefix: string) => { if (!isObject(obj)) return obj; let newObj: Record = mapValues( pickBy( obj, (v, k) => ('languages_code' in obj ? true : v !== null) && (k === 'children' ? v?.length > 0 : true), ), (v) => { return typeof v === 'string' ? translateLink(v, locale) : Array.isArray(v) ? v.map((vv) => transformHeaderMenuObject(vv, locale, prefix)) : isObject(v) ? transformHeaderMenuObject(v, locale, prefix) : v; }, ); if (newObj?.image?.filename_disk) { newObj.image = `${prefix}/${newObj?.image?.filename_disk}`; } if (newObj?.icon?.filename_disk) { newObj.icon = `${prefix}/${newObj?.icon?.filename_disk}`; } if (newObj?.src?.filename_disk) { newObj.src = `${prefix}/${newObj?.src?.filename_disk}`; } if (newObj?.thumbnail) { newObj = { ...omit(newObj, ['thumbnail']), ...transformHeaderMenuObject(newObj?.thumbnail, locale, prefix), }; } if (newObj?.translations?.length) { newObj = { ...omit(newObj, ['translations']), ...translateFields( newObj?.translations, Object.keys( omitBy( newObj?.translations[0], (v, k) => k.endsWith('_id') || ['id', 'languages_code'].includes(k), ), ), locale, ), }; } return newObj; }; const transformHeaderMenu = ( data: Record[], locale: string, prefix: string, ): any[] => { return data.map((obj) => transformHeaderMenuObject(obj, locale, prefix)); }; const getHeaderMenus = async ( client: DirectusClient & RestClient, locale: string, prefix: string, ) => { // fetch from server if (typeof window !== 'undefined') return Promise.resolve([]); const thumbnailImage = ['*.*', { translations: ['*'] }]; const thumbnailLink = ['*', { translations: ['*'] }]; const thumbnail = [ 'selfLink', 'thumbnailAnalyticsId', { translations: ['*'] }, { thumbnailImage }, { thumbnailLink }, ]; const tag = ['link', 'variant', 'target', { translations: ['*'] }]; const wallet = [ { options: ['analyticsId', 'chain', 'key', { translations: ['*'] }] }, { translations: ['*'] }, ]; const image = ['*']; const button = ['*']; const level1 = [ 'id', 'analyticsId', { thumbnail }, { image }, 'key', { button }, { translations: ['*'] }, ]; const level2 = [ 'id', { tag }, 'target', { icon: image }, 'analyticsId', 'link', 'subLink', 'selfLink', 'selfAnalyticsId', { thumbnail }, { wallet }, { button }, { translations: ['*'] }, ]; const level3 = ['id', 'target', 'link', 'analyticsId', { tag }, { translations: ['*'] }]; const _getMenus = () => { return client.request( // @ts-ignore ts v5 readItems('header_nav_level1', { fields: [...level1, { children: [...level2, { children: level3 }] }], }), ); }; try { if (headerMenus.length) { _getMenus(); transformHeaderMenu(headerMenus, locale, prefix); } const result = await _getMenus(); return transformHeaderMenu(result, locale, prefix); } catch { return []; } }; let cacheHeaderMenus: Record = {}; let cacheFooterMenus: Record = {}; let cacheFooterMedia: Record = {}; const doGetMenus = async (endpoint: string, locale: string, prefix: string) => { const client = createDirectus(endpoint).with(rest()); const headerMenus: Level1Props[] = await getHeaderMenus(client, locale, prefix); const footerMenus: FooterMenuProps[] = await getFooterMenus(client, locale); const footerMedia: FooterMediaItemProps[] = await getFooterMediaList(client, prefix); return { headerMenus, footerMenus, footerMedia }; }; const _getMenus = throttle(async (endpoint, locale, prefix) => { return { res: await doGetMenus(endpoint, locale, prefix), locale, }; }, 10000); const updateCmsConfig = ( headerMenus: Level1Props[], footerMenus: FooterMenuProps[], footerMedia: FooterMediaItemProps[], lang: string, ) => { cacheHeaderMenus = { ...cacheHeaderMenus, [lang]: headerMenus }; cacheFooterMenus = { ...cacheFooterMenus, [lang]: footerMenus }; cacheFooterMedia = { ...cacheFooterMedia, [lang]: footerMedia }; }; export const getMenus = async (endpoint: string, locale: string, prefix: string) => { if (cacheHeaderMenus[locale]?.length) { _getMenus(endpoint, locale, prefix)?.then(({ res, locale }) => { const { headerMenus, footerMenus, footerMedia } = res; updateCmsConfig(headerMenus, footerMenus, footerMedia, locale); }); return { headerMenus: cacheHeaderMenus[locale], footerMenus: cacheFooterMenus[locale], footerMedia: cacheFooterMedia[locale], }; } const { headerMenus, footerMenus, footerMedia } = await doGetMenus(endpoint, locale, prefix); updateCmsConfig(headerMenus, footerMenus, footerMedia, locale); return { headerMenus, footerMenus, footerMedia }; };