| 'use client'; |
|
|
| import useSWR from 'swr'; |
| import { useCallback, useMemo, useState } from 'react'; |
| import { capitalize, orderBy } from 'lodash'; |
| import clsx from 'clsx'; |
| import ImageWithFallback from '@gitroom/react/helpers/image.with.fallback'; |
| import SafeImage from '@gitroom/react/helpers/safe.image'; |
| import { useFetch } from '@gitroom/helpers/utils/custom.fetch'; |
| import { RenderAnalytics } from '@gitroom/frontend/components/platform-analytics/render.analytics'; |
| import { Select } from '@gitroom/react/form/select'; |
| import { Button } from '@gitroom/react/form/button'; |
| import { useRouter } from 'next/navigation'; |
| import { useToaster } from '@gitroom/react/toaster/toaster'; |
| import { useT } from '@gitroom/react/translation/get.transation.service.client'; |
| import { useVariables } from '@gitroom/react/helpers/variable.context'; |
| import useCookie from 'react-use-cookie'; |
| import { SVGLine } from '@gitroom/frontend/components/launches/launches.component'; |
| import { LoadingComponent } from '@gitroom/frontend/components/layout/loading'; |
| const allowedIntegrations = [ |
| 'facebook', |
| 'instagram', |
| 'instagram-standalone', |
| 'linkedin-page', |
| 'tiktok', |
| 'youtube', |
| 'gmb', |
| 'pinterest', |
| 'threads', |
| 'x', |
| ]; |
| export const PlatformAnalytics = () => { |
| const fetch = useFetch(); |
| const t = useT(); |
| const router = useRouter(); |
| const { disableXAnalytics } = useVariables(); |
|
|
| const [current, setCurrent] = useState(0); |
| const [key, setKey] = useState(7); |
| const [refresh, setRefresh] = useState(false); |
| const [collapseMenu, setCollapseMenu] = useCookie('collapseMenu', '0'); |
| const toaster = useToaster(); |
| const load = useCallback(async () => { |
| const int = ( |
| await (await fetch('/integrations/list')).json() |
| ).integrations.filter((f: any) => { |
| if (f.identifier === 'x' && disableXAnalytics) { |
| return false; |
| } |
| return true; |
| }); |
| return int.filter((f: any) => allowedIntegrations.includes(f.identifier)); |
| }, []); |
| const { data, isLoading } = useSWR('analytics-list', load, { |
| revalidateOnFocus: false, |
| revalidateOnReconnect: false, |
| revalidateIfStale: false, |
| revalidateOnMount: true, |
| refreshWhenHidden: false, |
| refreshWhenOffline: false, |
| fallbackData: [], |
| }); |
| const sortedIntegrations = useMemo(() => { |
| return orderBy( |
| data, |
| ['type', 'disabled', 'identifier'], |
| ['desc', 'asc', 'asc'] |
| ); |
| }, [data]); |
| const currentIntegration = useMemo(() => { |
| return sortedIntegrations[current]; |
| }, [current, sortedIntegrations]); |
| const options = useMemo(() => { |
| if (!currentIntegration) { |
| return []; |
| } |
| const arr = []; |
| if ( |
| [ |
| 'facebook', |
| 'instagram', |
| 'instagram-standalone', |
| 'linkedin-page', |
| 'pinterest', |
| 'youtube', |
| 'threads', |
| 'gmb', |
| 'x', |
| 'tiktok', |
| ].indexOf(currentIntegration.identifier) !== -1 |
| ) { |
| arr.push({ |
| key: 7, |
| value: t('7_days', '7 Days'), |
| }); |
| } |
| if ( |
| [ |
| 'facebook', |
| 'instagram', |
| 'instagram-standalone', |
| 'linkedin-page', |
| 'pinterest', |
| 'youtube', |
| 'threads', |
| 'gmb', |
| 'x', |
| 'tiktok', |
| ].indexOf(currentIntegration.identifier) !== -1 |
| ) { |
| arr.push({ |
| key: 30, |
| value: t('30_days', '30 Days'), |
| }); |
| } |
| if ( |
| ['facebook', 'linkedin-page', 'pinterest', 'youtube', 'x', 'gmb'].indexOf( |
| currentIntegration.identifier |
| ) !== -1 |
| ) { |
| arr.push({ |
| key: 90, |
| value: t('90_days', '90 Days'), |
| }); |
| } |
| return arr; |
| }, [currentIntegration]); |
| const keys = useMemo(() => { |
| if (!currentIntegration) { |
| return 7; |
| } |
| if (options.find((p) => p.key === key)) { |
| return key; |
| } |
| return options[0]?.key; |
| }, [key, currentIntegration]); |
|
|
| if (isLoading) { |
| return ( |
| <div className="bg-newBgColorInner p-[20px] flex flex-1 flex-col gap-[15px] transition-all items-center justify-center"> |
| <LoadingComponent /> |
| </div> |
| ); |
| } |
|
|
| if (!sortedIntegrations.length && !isLoading) { |
| return ( |
| <div className="bg-newBgColorInner p-[20px] flex flex-col gap-[15px] transition-all flex-1 justify-center items-center text-center"> |
| <div> |
| <img src="/peoplemarketplace.svg" /> |
| </div> |
| <div className="text-[48px]"> |
| {t('can_t_show_analytics_yet', "Can't show analytics yet")} |
| <br /> |
| {t( |
| 'you_have_to_add_social_media_channels', |
| 'You have to add Social Media channels' |
| )} |
| </div> |
| <div className="text-[20px]"> |
| {t('supported', 'Supported:')} |
| {allowedIntegrations.map((p) => capitalize(p)).join(', ')} |
| </div> |
| <Button onClick={() => router.push('/launches')}> |
| {t( |
| 'go_to_the_calendar_to_add_channels', |
| 'Go to the calendar to add channels' |
| )} |
| </Button> |
| </div> |
| ); |
| } |
| return ( |
| <> |
| <div |
| className={clsx( |
| 'bg-newBgColorInner p-[20px] flex flex-col gap-[15px] transition-all', |
| collapseMenu === '1' ? 'group sidebar w-[100px]' : 'w-[260px]' |
| )} |
| > |
| <div className="flex gap-[12px] flex-col"> |
| <div className="flex items-center"> |
| <h2 className="group-[.sidebar]:hidden flex-1 text-[20px] font-[500]"> |
| {t('channels')} |
| </h2> |
| <div |
| onClick={() => setCollapseMenu(collapseMenu === '1' ? '0' : '1')} |
| className="group-[.sidebar]:rotate-[180deg] group-[.sidebar]:mx-auto text-btnText bg-btnSimple rounded-[6px] w-[24px] h-[24px] flex items-center justify-center cursor-pointer select-none" |
| > |
| <svg |
| xmlns="http://www.w3.org/2000/svg" |
| width="7" |
| height="13" |
| viewBox="0 0 7 13" |
| fill="none" |
| > |
| <path |
| d="M6 11.5L1 6.5L6 1.5" |
| stroke="currentColor" |
| strokeWidth="1.5" |
| strokeLinecap="round" |
| strokeLinejoin="round" |
| /> |
| </svg> |
| </div> |
| </div> |
| {sortedIntegrations.map((integration, index) => ( |
| <div |
| key={integration.id} |
| onClick={() => { |
| if (integration.refreshNeeded) { |
| toaster.show( |
| 'Please refresh the integration from the calendar', |
| 'warning' |
| ); |
| return; |
| } |
| setRefresh(true); |
| setTimeout(() => { |
| setRefresh(false); |
| }, 10); |
| setCurrent(index); |
| }} |
| className={clsx( |
| 'flex gap-[12px] items-center group/profile justify-center hover:bg-boxHover rounded-e-[8px]', |
| currentIntegration.id !== integration.id && |
| 'opacity-20 hover:opacity-100 cursor-pointer' |
| )} |
| > |
| <div |
| className={clsx( |
| 'relative rounded-full flex justify-center items-center gap-[6px]', |
| integration.disabled && 'opacity-50' |
| )} |
| > |
| {(integration.inBetweenSteps || integration.refreshNeeded) && ( |
| <div className="absolute start-0 top-0 w-[39px] h-[46px] cursor-pointer"> |
| <div className="bg-red-500 w-[15px] h-[15px] rounded-full start-0 -top-[5px] absolute z-[200] text-[10px] flex justify-center items-center"> |
| ! |
| </div> |
| <div className="bg-primary/60 w-[39px] h-[46px] start-0 top-0 absolute rounded-full z-[199]" /> |
| </div> |
| )} |
| <div className="h-full w-[4px] -ms-[12px] rounded-s-[3px] opacity-0 group-hover/profile:opacity-100 transition-opacity"> |
| <SVGLine /> |
| </div> |
| <ImageWithFallback |
| fallbackSrc={`/icons/platforms/${integration.identifier}.png`} |
| src={integration.picture} |
| className="rounded-[8px]" |
| alt={integration.identifier} |
| width={36} |
| height={36} |
| /> |
| <SafeImage |
| src={`/icons/platforms/${integration.identifier}.png`} |
| className="rounded-[8px] absolute z-10 bottom-[5px] -end-[5px] border border-fifth" |
| alt={integration.identifier} |
| width={18.41} |
| height={18.41} |
| /> |
| </div> |
| <div |
| className={clsx( |
| 'flex-1 whitespace-nowrap text-ellipsis overflow-hidden group-[.sidebar]:hidden', |
| integration.disabled && 'opacity-50' |
| )} |
| > |
| {integration.name} |
| </div> |
| </div> |
| ))} |
| </div> |
| </div> |
| <div className="bg-newBgColorInner flex-1 flex-col flex p-[20px] gap-[12px]"> |
| {!!options.length && ( |
| <div className="flex-1 flex flex-col gap-[14px]"> |
| <div className="max-w-[200px]"> |
| <Select |
| label="" |
| name="date" |
| disableForm={true} |
| hideErrors={true} |
| onChange={(e) => setKey(+e.target.value)} |
| > |
| {options.map((option) => ( |
| <option key={option.key} value={option.key}> |
| {option.value} |
| </option> |
| ))} |
| </Select> |
| </div> |
| <div className="flex-1"> |
| {!!keys && !!currentIntegration && !refresh && ( |
| <RenderAnalytics integration={currentIntegration} date={keys} /> |
| )} |
| </div> |
| </div> |
| )} |
| </div> |
| </> |
| ); |
| }; |
|
|