Download apps/frontend/src/components/platform-analytics/platform.analytics.tsx from Leon4gr45/postio: direct link, hf CLI and curl.
- Browser
- Download file 10.1 kB
-
https://huggingface.co/spaces/Leon4gr45/postio/resolve/main/apps/frontend/src/components/platform-analytics/platform.analytics.tsx
- Command line
-
hf download hf://spaces/Leon4gr45/postio/apps/frontend/src/components/platform-analytics/platform.analytics.tsx
-
curl -L -o platform.analytics.tsx https://huggingface.co/spaces/Leon4gr45/postio/resolve/main/apps/frontend/src/components/platform-analytics/platform.analytics.tsx
10.1 kB
| '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> | |
| </> | |
| ); | |
| }; | |