feat: better performance

This commit is contained in:
Nevo David 2024-03-09 22:27:06 +07:00
parent 1ae0638d3c
commit 7a9382ddf1
15 changed files with 387 additions and 255 deletions

View File

@ -1,5 +1,4 @@
import {AnalyticsComponent} from "@gitroom/frontend/components/analytics/analytics.component";
import {internalFetch} from "@gitroom/helpers/utils/internal.fetch";
import {Metadata} from "next";
export const metadata: Metadata = {
@ -8,14 +7,7 @@ export const metadata: Metadata = {
}
export default async function Index() {
const analytics = await (await internalFetch('/analytics')).json();
const trending = await (await internalFetch('/analytics/trending')).json();
const stars = await (await internalFetch('/analytics/stars', {
body: JSON.stringify({page: 1}),
method: 'POST'
})).json();
return (
<AnalyticsComponent list={analytics} trending={trending} stars={stars.stars} />
<AnalyticsComponent />
);
}

View File

@ -1,7 +1,5 @@
import { internalFetch } from '@gitroom/helpers/utils/internal.fetch';
import { BillingComponent } from '@gitroom/frontend/components/billing/billing.component';
import { Metadata } from 'next';
import { redirect } from 'next/navigation';
export const metadata: Metadata = {
title: 'Gitroom Billing',
@ -9,13 +7,5 @@ export const metadata: Metadata = {
};
export default async function Page() {
const tiers = await (await internalFetch('/user/subscription/tiers')).json();
if (tiers?.statusCode === 402) {
return redirect('/');
}
const { subscription } = await (
await internalFetch('/user/subscription')
).json();
return <BillingComponent subscription={subscription} tiers={tiers} />;
return <BillingComponent />;
}

View File

@ -1,5 +1,4 @@
import {LaunchesComponent} from "@gitroom/frontend/components/launches/launches.component";
import {internalFetch} from "@gitroom/helpers/utils/internal.fetch";
import {Metadata} from "next";
export const metadata: Metadata = {
@ -8,8 +7,7 @@ export const metadata: Metadata = {
}
export default async function Index() {
const {integrations} = await (await internalFetch('/integrations/list')).json();
return (
<LaunchesComponent integrations={integrations} />
<LaunchesComponent />
);
}

View File

@ -0,0 +1,5 @@
import { LoadingComponent } from '@gitroom/frontend/components/layout/loading';
export default function Loading() {
return <LoadingComponent />;
}

View File

@ -22,16 +22,5 @@ export default async function Index({
return redirect('/settings', RedirectType.replace);
}
const { github } = await (await internalFetch('/settings/github')).json();
if (!github) {
return redirect('/');
}
const emptyOnes = github.find((p: { login: string }) => !p.login);
const { organizations } = emptyOnes
? await (
await internalFetch(`/settings/organizations/${emptyOnes.id}`)
).json()
: { organizations: [] };
return <SettingsComponent github={github} organizations={organizations} />;
return <SettingsComponent />;
}

View File

@ -1,105 +1,141 @@
import {StarsAndForks} from "@gitroom/frontend/components/analytics/stars.and.forks";
import {FC} from "react";
import {StarsAndForksInterface} from "@gitroom/frontend/components/analytics/stars.and.forks.interface";
import {StarsTableComponent} from "@gitroom/frontend/components/analytics/stars.table.component";
'use client';
export const AnalyticsComponent: FC<StarsAndForksInterface> = (props) => {
return (
<div className="flex gap-[24px] flex-1">
<div className="flex flex-col gap-[24px] flex-1">
<StarsAndForks {...props} />
<div className="flex flex-1 flex-col gap-[15px] min-h-[426px]">
<h2 className="text-[24px]">Stars per day</h2>
<div className="flex-1 bg-secondary">
<StarsTableComponent stars={props.stars} />
</div>
</div>
</div>
{/*<div className="w-[318px] bg-third mt-[-44px] p-[16px]">*/}
{/* <h2 className="text-[20px]">News Feed</h2>*/}
{/* <div className="my-[30px] flex h-[32px]">*/}
{/* <div className="flex-1 bg-forth flex justify-center items-center">*/}
{/* Global*/}
{/* </div>*/}
{/* <div className="flex-1 bg-primary flex justify-center items-center">*/}
{/* My Feed*/}
{/* </div>*/}
{/* </div>*/}
{/* <div>*/}
{/* <div className="w-full flex-col justify-start items-start gap-4 inline-flex">*/}
{/* <div className="self-stretch justify-start items-start gap-2.5 inline-flex pb-[16.5px] border-b-[1px] border-[#28344F]">*/}
{/* <img className="w-8 h-8 rounded-full" src="https://via.placeholder.com/32x32"/>*/}
{/* <div className="grow shrink basis-0 flex-col justify-start items-start gap-1 inline-flex">*/}
{/* <div className="justify-center items-center gap-1 inline-flex">*/}
{/* <div className="text-white text-sm font-medium leading-tight">Nevo David</div>*/}
{/* <div className="text-neutral-500 text-[10px] font-normal uppercase tracking-wide">05/06/2024</div>*/}
{/* </div>*/}
{/* <div className="self-stretch text-neutral-400 text-xs font-normal">O atual sistema político precisa mudar para valorizar o trabalho e garantir igualdade de oportunidad</div>*/}
{/* <div className="self-stretch justify-start items-center gap-1 inline-flex">*/}
{/* <div className="text-[#E4B895] text-xs font-normal">See Tweet</div>*/}
{/* <div className="w-4 h-4 relative"/>*/}
{/* </div>*/}
{/* </div>*/}
{/* </div>*/}
{/* <div className="self-stretch justify-start items-start gap-2.5 inline-flex pb-[16.5px] border-b-[1px] border-[#28344F]">*/}
{/* <img className="w-8 h-8 rounded-full" src="https://via.placeholder.com/32x32"/>*/}
{/* <div className="grow shrink basis-0 flex-col justify-start items-start gap-1 inline-flex">*/}
{/* <div className="justify-center items-center gap-1 inline-flex">*/}
{/* <div className="text-white text-sm font-medium leading-tight">Nevo David</div>*/}
{/* <div className="text-neutral-500 text-[10px] font-normal uppercase tracking-wide">05/06/2024</div>*/}
{/* </div>*/}
{/* <div className="self-stretch text-neutral-400 text-xs font-normal">O atual sistema político precisa mudar para valorizar o trabalho e garantir igualdade de oportunidad</div>*/}
{/* <div className="self-stretch justify-start items-center gap-1 inline-flex">*/}
{/* <div className="text-[#E4B895] text-xs font-normal">See Tweet</div>*/}
{/* <div className="w-4 h-4 relative"/>*/}
{/* </div>*/}
{/* </div>*/}
{/* </div>*/}
{/* <div className="self-stretch justify-start items-start gap-2.5 inline-flex pb-[16.5px] border-b-[1px] border-[#28344F]">*/}
{/* <img className="w-8 h-8 rounded-full" src="https://via.placeholder.com/32x32"/>*/}
{/* <div className="grow shrink basis-0 flex-col justify-start items-start gap-1 inline-flex">*/}
{/* <div className="justify-center items-center gap-1 inline-flex">*/}
{/* <div className="text-white text-sm font-medium leading-tight">Nevo David</div>*/}
{/* <div className="text-neutral-500 text-[10px] font-normal uppercase tracking-wide">05/06/2024</div>*/}
{/* </div>*/}
{/* <div className="self-stretch text-neutral-400 text-xs font-normal">O atual sistema político precisa mudar para valorizar o trabalho e garantir igualdade de oportunidad</div>*/}
{/* <div className="self-stretch justify-start items-center gap-1 inline-flex">*/}
{/* <div className="text-[#E4B895] text-xs font-normal">See Tweet</div>*/}
{/* <div className="w-4 h-4 relative"/>*/}
{/* </div>*/}
{/* </div>*/}
{/* </div>*/}
{/* <div className="self-stretch justify-start items-start gap-2.5 inline-flex pb-[16.5px] border-b-[1px] border-[#28344F]">*/}
{/* <img className="w-8 h-8 rounded-full" src="https://via.placeholder.com/32x32"/>*/}
{/* <div className="grow shrink basis-0 flex-col justify-start items-start gap-1 inline-flex">*/}
{/* <div className="justify-center items-center gap-1 inline-flex">*/}
{/* <div className="text-white text-sm font-medium leading-tight">Nevo David</div>*/}
{/* <div className="text-neutral-500 text-[10px] font-normal uppercase tracking-wide">05/06/2024</div>*/}
{/* </div>*/}
{/* <div className="self-stretch text-neutral-400 text-xs font-normal">O atual sistema político precisa mudar para valorizar o trabalho e garantir igualdade de oportunidad</div>*/}
{/* <div className="self-stretch justify-start items-center gap-1 inline-flex">*/}
{/* <div className="text-[#E4B895] text-xs font-normal">See Tweet</div>*/}
{/* <div className="w-4 h-4 relative"/>*/}
{/* </div>*/}
{/* </div>*/}
{/* </div>*/}
{/* <div className="self-stretch justify-start items-start gap-2.5 inline-flex pb-[16.5px] border-b-[1px] border-[#28344F]">*/}
{/* <img className="w-8 h-8 rounded-full" src="https://via.placeholder.com/32x32"/>*/}
{/* <div className="grow shrink basis-0 flex-col justify-start items-start gap-1 inline-flex">*/}
{/* <div className="justify-center items-center gap-1 inline-flex">*/}
{/* <div className="text-white text-sm font-medium leading-tight">Nevo David</div>*/}
{/* <div className="text-neutral-500 text-[10px] font-normal uppercase tracking-wide">05/06/2024</div>*/}
{/* </div>*/}
{/* <div className="self-stretch text-neutral-400 text-xs font-normal">O atual sistema político precisa mudar para valorizar o trabalho e garantir igualdade de oportunidad</div>*/}
{/* <div className="self-stretch justify-start items-center gap-1 inline-flex">*/}
{/* <div className="text-[#E4B895] text-xs font-normal">See Tweet</div>*/}
{/* <div className="w-4 h-4 relative"/>*/}
{/* </div>*/}
{/* </div>*/}
{/* </div>*/}
{/* </div>*/}
{/* </div>*/}
{/*</div>*/}
import { StarsAndForks } from '@gitroom/frontend/components/analytics/stars.and.forks';
import { FC, useCallback } from 'react';
import { StarsTableComponent } from '@gitroom/frontend/components/analytics/stars.table.component';
import useSWR from 'swr';
import { useFetch } from '@gitroom/helpers/utils/custom.fetch';
import { LoadingComponent } from '@gitroom/frontend/components/layout/loading';
export const AnalyticsComponent: FC = () => {
const fetch = useFetch();
const load = useCallback(async (path: string) => {
return await (await fetch(path)).json();
}, []);
const starsCallback = useCallback(async (path: string) => {
return await (
await fetch(path, {
body: JSON.stringify({ page: 1 }),
method: 'POST',
})
).json();
}, []);
const { isLoading: isLoadingAnalytics, data: analytics } = useSWR(
'/analytics',
load
);
const { isLoading: isLoadingTrending, data: trending } = useSWR(
'/analytics/trending',
load
);
const { isLoading: isLoadingStars, data: stars } = useSWR(
'/analytics/stars',
starsCallback
);
if (isLoadingAnalytics || isLoadingTrending || isLoadingStars) {
return <LoadingComponent />;
}
return (
<div className="flex gap-[24px] flex-1">
<div className="flex flex-col gap-[24px] flex-1">
<StarsAndForks list={analytics} stars={stars.stars} trending={trending} />
<div className="flex flex-1 flex-col gap-[15px] min-h-[426px]">
<h2 className="text-[24px]">Stars per day</h2>
<div className="flex-1 bg-secondary">
<StarsTableComponent stars={stars.stars} />
</div>
</div>
);
}
</div>
{/*<div className="w-[318px] bg-third mt-[-44px] p-[16px]">*/}
{/* <h2 className="text-[20px]">News Feed</h2>*/}
{/* <div className="my-[30px] flex h-[32px]">*/}
{/* <div className="flex-1 bg-forth flex justify-center items-center">*/}
{/* Global*/}
{/* </div>*/}
{/* <div className="flex-1 bg-primary flex justify-center items-center">*/}
{/* My Feed*/}
{/* </div>*/}
{/* </div>*/}
{/* <div>*/}
{/* <div className="w-full flex-col justify-start items-start gap-4 inline-flex">*/}
{/* <div className="self-stretch justify-start items-start gap-2.5 inline-flex pb-[16.5px] border-b-[1px] border-[#28344F]">*/}
{/* <img className="w-8 h-8 rounded-full" src="https://via.placeholder.com/32x32"/>*/}
{/* <div className="grow shrink basis-0 flex-col justify-start items-start gap-1 inline-flex">*/}
{/* <div className="justify-center items-center gap-1 inline-flex">*/}
{/* <div className="text-white text-sm font-medium leading-tight">Nevo David</div>*/}
{/* <div className="text-neutral-500 text-[10px] font-normal uppercase tracking-wide">05/06/2024</div>*/}
{/* </div>*/}
{/* <div className="self-stretch text-neutral-400 text-xs font-normal">O atual sistema político precisa mudar para valorizar o trabalho e garantir igualdade de oportunidad</div>*/}
{/* <div className="self-stretch justify-start items-center gap-1 inline-flex">*/}
{/* <div className="text-[#E4B895] text-xs font-normal">See Tweet</div>*/}
{/* <div className="w-4 h-4 relative"/>*/}
{/* </div>*/}
{/* </div>*/}
{/* </div>*/}
{/* <div className="self-stretch justify-start items-start gap-2.5 inline-flex pb-[16.5px] border-b-[1px] border-[#28344F]">*/}
{/* <img className="w-8 h-8 rounded-full" src="https://via.placeholder.com/32x32"/>*/}
{/* <div className="grow shrink basis-0 flex-col justify-start items-start gap-1 inline-flex">*/}
{/* <div className="justify-center items-center gap-1 inline-flex">*/}
{/* <div className="text-white text-sm font-medium leading-tight">Nevo David</div>*/}
{/* <div className="text-neutral-500 text-[10px] font-normal uppercase tracking-wide">05/06/2024</div>*/}
{/* </div>*/}
{/* <div className="self-stretch text-neutral-400 text-xs font-normal">O atual sistema político precisa mudar para valorizar o trabalho e garantir igualdade de oportunidad</div>*/}
{/* <div className="self-stretch justify-start items-center gap-1 inline-flex">*/}
{/* <div className="text-[#E4B895] text-xs font-normal">See Tweet</div>*/}
{/* <div className="w-4 h-4 relative"/>*/}
{/* </div>*/}
{/* </div>*/}
{/* </div>*/}
{/* <div className="self-stretch justify-start items-start gap-2.5 inline-flex pb-[16.5px] border-b-[1px] border-[#28344F]">*/}
{/* <img className="w-8 h-8 rounded-full" src="https://via.placeholder.com/32x32"/>*/}
{/* <div className="grow shrink basis-0 flex-col justify-start items-start gap-1 inline-flex">*/}
{/* <div className="justify-center items-center gap-1 inline-flex">*/}
{/* <div className="text-white text-sm font-medium leading-tight">Nevo David</div>*/}
{/* <div className="text-neutral-500 text-[10px] font-normal uppercase tracking-wide">05/06/2024</div>*/}
{/* </div>*/}
{/* <div className="self-stretch text-neutral-400 text-xs font-normal">O atual sistema político precisa mudar para valorizar o trabalho e garantir igualdade de oportunidad</div>*/}
{/* <div className="self-stretch justify-start items-center gap-1 inline-flex">*/}
{/* <div className="text-[#E4B895] text-xs font-normal">See Tweet</div>*/}
{/* <div className="w-4 h-4 relative"/>*/}
{/* </div>*/}
{/* </div>*/}
{/* </div>*/}
{/* <div className="self-stretch justify-start items-start gap-2.5 inline-flex pb-[16.5px] border-b-[1px] border-[#28344F]">*/}
{/* <img className="w-8 h-8 rounded-full" src="https://via.placeholder.com/32x32"/>*/}
{/* <div className="grow shrink basis-0 flex-col justify-start items-start gap-1 inline-flex">*/}
{/* <div className="justify-center items-center gap-1 inline-flex">*/}
{/* <div className="text-white text-sm font-medium leading-tight">Nevo David</div>*/}
{/* <div className="text-neutral-500 text-[10px] font-normal uppercase tracking-wide">05/06/2024</div>*/}
{/* </div>*/}
{/* <div className="self-stretch text-neutral-400 text-xs font-normal">O atual sistema político precisa mudar para valorizar o trabalho e garantir igualdade de oportunidad</div>*/}
{/* <div className="self-stretch justify-start items-center gap-1 inline-flex">*/}
{/* <div className="text-[#E4B895] text-xs font-normal">See Tweet</div>*/}
{/* <div className="w-4 h-4 relative"/>*/}
{/* </div>*/}
{/* </div>*/}
{/* </div>*/}
{/* <div className="self-stretch justify-start items-start gap-2.5 inline-flex pb-[16.5px] border-b-[1px] border-[#28344F]">*/}
{/* <img className="w-8 h-8 rounded-full" src="https://via.placeholder.com/32x32"/>*/}
{/* <div className="grow shrink basis-0 flex-col justify-start items-start gap-1 inline-flex">*/}
{/* <div className="justify-center items-center gap-1 inline-flex">*/}
{/* <div className="text-white text-sm font-medium leading-tight">Nevo David</div>*/}
{/* <div className="text-neutral-500 text-[10px] font-normal uppercase tracking-wide">05/06/2024</div>*/}
{/* </div>*/}
{/* <div className="self-stretch text-neutral-400 text-xs font-normal">O atual sistema político precisa mudar para valorizar o trabalho e garantir igualdade de oportunidad</div>*/}
{/* <div className="self-stretch justify-start items-center gap-1 inline-flex">*/}
{/* <div className="text-[#E4B895] text-xs font-normal">See Tweet</div>*/}
{/* <div className="w-4 h-4 relative"/>*/}
{/* </div>*/}
{/* </div>*/}
{/* </div>*/}
{/* </div>*/}
{/* </div>*/}
{/*</div>*/}
</div>
);
};

View File

@ -1,65 +1,123 @@
import {FC} from "react";
import {StarsAndForksInterface} from "@gitroom/frontend/components/analytics/stars.and.forks.interface";
import {Chart} from "@gitroom/frontend/components/analytics/chart";
import Image from "next/image";
import {UtcToLocalDateRender} from "../../../../../libraries/react-shared-libraries/src/helpers/utc.date.render";
import clsx from "clsx";
import { FC } from 'react';
import { StarsAndForksInterface } from '@gitroom/frontend/components/analytics/stars.and.forks.interface';
import { Chart } from '@gitroom/frontend/components/analytics/chart';
import Image from 'next/image';
import { UtcToLocalDateRender } from '../../../../../libraries/react-shared-libraries/src/helpers/utc.date.render';
import clsx from 'clsx';
export const StarsAndForks: FC<StarsAndForksInterface> = (props) => {
const {list} = props;
return (
<>
{list.map(item => (
<div className="flex gap-[24px] h-[272px]" key={item.login}>
{[1,2].map(p => (
<div key={p} className="flex-1 bg-secondary py-[10px] px-[16px] flex flex-col">
<div className="flex items-center gap-[14px]">
<div>
<Image src="/icons/star-circle.svg" alt="Stars" width={40} height={40}/>
</div>
<div className="text-[20px]">
{item.login.split('/')[1].split('').map(((char, index) => index === 0 ? char.toUpperCase() : char)).join('')} {p === 1 ? 'Stars' : 'Forks'}
</div>
</div>
<div className="flex-1 relative">
<div className="absolute w-full h-full left-0 top-0">
{item.stars.length ? <Chart list={item.stars}/> : <div className="w-full h-full flex items-center justify-center text-3xl">Processing stars...</div>}
</div>
</div>
<div className="text-[50px] leading-[60px]">
{item?.stars[item.stars.length - 1]?.totalStars}
</div>
</div>
))}
const { list } = props;
return (
<>
{list.map((item) => (
<div className="flex gap-[24px] h-[272px]" key={item.login}>
{[1, 2].map((p) => (
<div
key={p}
className="flex-1 bg-secondary py-[10px] px-[16px] flex flex-col"
>
<div className="flex items-center gap-[14px]">
<div>
<Image
src="/icons/star-circle.svg"
alt="Stars"
width={40}
height={40}
/>
</div>
))}
<div className="flex gap-[24px]">
{[0, 1].map( p => (
<div key={p} className="flex-1 bg-secondary py-[24px] px-[16px] gap-[16px] flex flex-col">
<div className="flex items-center gap-[14px]">
<div>
<Image src="/icons/trending.svg" width={36} height={36} alt="Trending" />
</div>
<div className="text-[20px]">
{p === 0 ? 'Last Github Trending' : 'Next Predicted GitHub Trending'}
</div>
</div>
<div className="flex items-center">
<div className="w-[2px] h-[30px] bg-[#8B90FF] mr-[16px]"></div>
<div className="text-[24px] flex-1">
<UtcToLocalDateRender date={p === 0 ? props.trending.last : props.trending.predictions} format="dddd"/>
</div>
<div className={clsx("text-[24px]", p === 0 ? 'text-[#B7C1FF]' : 'text-[#FFAC30]')}>
<UtcToLocalDateRender date={p === 0 ? props.trending.last : props.trending.predictions} format="DD MMM YYYY"/>
</div>
<div>
<div className="rounded-full bg-[#576A9A] w-[5px] h-[5px] mx-[8px]"/>
</div>
<div className={clsx("text-[24px]", p === 0 ? 'text-[#B7C1FF]' : 'text-[#FFAC30]')}>
<UtcToLocalDateRender date={p === 0 ? props.trending.last : props.trending.predictions} format="HH:mm"/>
</div>
</div>
</div>))}
<div className="text-[20px]">
{item.login
.split('/')[1]
.split('')
.map((char, index) =>
index === 0 ? char.toUpperCase() : char
)
.join('')}{' '}
{p === 1 ? 'Stars' : 'Forks'}
</div>
</div>
<div className="flex-1 relative">
<div className="absolute w-full h-full left-0 top-0">
{item.stars.length ? (
<Chart list={item.stars} />
) : (
<div className="w-full h-full flex items-center justify-center text-3xl">
Processing stars...
</div>
)}
</div>
</div>
<div className="text-[50px] leading-[60px]">
{item?.stars[item.stars.length - 1]?.totalStars}
</div>
</div>
</>);
}
))}
</div>
))}
<div className="flex gap-[24px]">
{[0, 1].map((p) => (
<div
key={p}
className="flex-1 bg-secondary py-[24px] px-[16px] gap-[16px] flex flex-col"
>
<div className="flex items-center gap-[14px]">
<div>
<Image
src="/icons/trending.svg"
width={36}
height={36}
alt="Trending"
/>
</div>
<div className="text-[20px]">
{p === 0
? 'Last Github Trending'
: 'Next Predicted GitHub Trending'}
</div>
</div>
<div className="flex items-center">
<div className="w-[2px] h-[30px] bg-[#8B90FF] mr-[16px]"></div>
<div className="text-[24px] flex-1">
<UtcToLocalDateRender
date={
p === 0 ? props.trending.last : props.trending.predictions
}
format="dddd"
/>
</div>
<div
className={clsx(
'text-[24px]',
p === 0 ? 'text-[#B7C1FF]' : 'text-[#FFAC30]'
)}
>
<UtcToLocalDateRender
date={
p === 0 ? props.trending.last : props.trending.predictions
}
format="DD MMM YYYY"
/>
</div>
<div>
<div className="rounded-full bg-[#576A9A] w-[5px] h-[5px] mx-[8px]" />
</div>
<div
className={clsx(
'text-[24px]',
p === 0 ? 'text-[#B7C1FF]' : 'text-[#FFAC30]'
)}
>
<UtcToLocalDateRender
date={
p === 0 ? props.trending.last : props.trending.predictions
}
format="HH:mm"
/>
</div>
</div>
</div>
))}
</div>
</>
);
};

View File

@ -1,14 +1,29 @@
import { FC } from 'react';
import { Subscription } from '@prisma/client';
import {
NoBillingComponent,
Tiers,
} from '@gitroom/frontend/components/billing/no.billing.component';
"use client";
export const BillingComponent: FC<{
subscription?: Subscription;
tiers: Tiers;
}> = (props) => {
const { subscription, tiers } = props;
return <NoBillingComponent tiers={tiers} sub={subscription} />;
import { useCallback } from 'react';
import { NoBillingComponent } from '@gitroom/frontend/components/billing/no.billing.component';
import useSWR from 'swr';
import { LoadingComponent } from '@gitroom/frontend/components/layout/loading';
import {useFetch} from "@gitroom/helpers/utils/custom.fetch";
export const BillingComponent = () => {
const fetch = useFetch();
const load = useCallback(async (path: string) => {
return await (await fetch(path)).json();
}, []);
const { isLoading: isLoadingTier, data: tiers } = useSWR(
'/user/subscription/tiers',
load
);
const { isLoading: isLoadingSubscription, data: subscription } = useSWR(
'/user/subscription',
load
);
if (isLoadingSubscription || isLoadingTier) {
return <LoadingComponent />;
}
return <NoBillingComponent tiers={tiers} sub={subscription?.subscription} />;
};

View File

@ -1,18 +1,30 @@
'use client';
import { AddProviderButton } from '@gitroom/frontend/components/launches/add.provider.component';
import { FC, useMemo } from 'react';
import { FC, useCallback, useMemo } from 'react';
import Image from 'next/image';
import { orderBy } from 'lodash';
import { Calendar } from '@gitroom/frontend/components/launches/calendar';
import {
CalendarWeekProvider,
Integrations,
} from '@gitroom/frontend/components/launches/calendar.context';
import { Filters } from '@gitroom/frontend/components/launches/filters';
import { useFetch } from '@gitroom/helpers/utils/custom.fetch';
import useSWR from 'swr';
export const LaunchesComponent: FC<{
integrations: Integrations[];
}> = (props) => {
const { integrations } = props;
export const LaunchesComponent = () => {
const fetch = useFetch();
const load = useCallback(async (path: string) => {
return (await (await fetch(path)).json()).integrations;
}, []);
const { data: integrations } = useSWR(
'/integrations/list',
load,
{
fallbackData: [],
}
);
const sortedIntegrations = useMemo(() => {
return orderBy(integrations, ['type', 'identifier'], ['desc', 'asc']);
@ -22,9 +34,7 @@ export const LaunchesComponent: FC<{
<CalendarWeekProvider integrations={sortedIntegrations}>
<div className="flex flex-1 flex-col">
<div className="flex flex-1 relative">
<div
className="absolute w-full h-full flex flex-1 gap-[30px] overflow-hidden overflow-y-scroll scrollbar scrollbar-thumb-tableBorder scrollbar-track-secondary"
>
<div className="absolute w-full h-full flex flex-1 gap-[30px] overflow-hidden overflow-y-scroll scrollbar scrollbar-thumb-tableBorder scrollbar-track-secondary">
<div className="w-[220px] bg-third p-[16px] flex flex-col gap-[24px] sticky top-0">
<h2 className="text-[20px]">Channels</h2>
<div className="gap-[16px] flex flex-col">

View File

@ -1,4 +1,6 @@
import { ReactNode } from 'react';
'use client';
import { ReactNode, useCallback } from 'react';
import { Title } from '@gitroom/frontend/components/layout/title';
import { headers } from 'next/headers';
import { ContextWrapper } from '@gitroom/frontend/components/layout/user.context';
@ -10,11 +12,22 @@ import Image from 'next/image';
import { Toaster } from '@gitroom/react/toaster/toaster';
import { ShowPostSelector } from '@gitroom/frontend/components/post-url-selector/post.url.selector';
import { OrganizationSelector } from '@gitroom/frontend/components/layout/organization.selector';
import NotificationComponent from "@gitroom/frontend/components/notifications/notification.component";
import Link from "next/link";
import NotificationComponent from '@gitroom/frontend/components/notifications/notification.component';
import Link from 'next/link';
import useSWR from 'swr';
import { useFetch } from '@gitroom/helpers/utils/custom.fetch';
export const LayoutSettings = ({ children }: { children: ReactNode }) => {
const user = JSON.parse(headers().get('user')!);
const fetch = useFetch();
const load = useCallback(async (path: string) => {
return await (await fetch(path)).json();
}, []);
const { data: user } = useSWR(
'/user/self',
load
);
return (
<ContextWrapper user={user}>
<MantineWrapper>

View File

@ -0,0 +1,6 @@
'use client';
import ReactLoading from 'react-loading';
export const LoadingComponent = () => {
return <div className="flex-1 flex justify-center pt-[100px]"><ReactLoading type="spin" color="#fff" width={100} height={100} /></div>;
};

View File

@ -48,7 +48,7 @@ export const TopMenu: FC = () => {
.map((item, index) => (
<li key={item.name}>
<Link
prefetch={false}
prefetch={true}
href={item.path}
className={clsx(
'flex gap-2 items-center box',

View File

@ -24,8 +24,9 @@ export const ContextWrapper: FC<{
};
children: ReactNode;
}> = ({ user, children }) => {
const values = user ? { ...user, tier: pricing[user.tier] } : ({} as any);
return (
<UserContext.Provider value={{ ...user, tier: pricing[user.tier] }}>
<UserContext.Provider value={values}>
{children}
</UserContext.Provider>
);

View File

@ -1,17 +1,52 @@
'use client';
import { Checkbox } from '@gitroom/react/form/checkbox';
import { GithubComponent } from '@gitroom/frontend/components/settings/github.component';
import { FC } from 'react';
import { useCallback, useEffect } from 'react';
import { useUser } from '@gitroom/frontend/components/layout/user.context';
import { TeamsComponent } from '@gitroom/frontend/components/settings/teams.component';
import { useFetch } from '@gitroom/helpers/utils/custom.fetch';
import useSWR from 'swr';
import { LoadingComponent } from '@gitroom/frontend/components/layout/loading';
import { useRouter } from 'next/navigation';
export const SettingsComponent: FC<{
organizations: Array<{ login: string; id: string }>;
github: Array<{ id: string; login: string }>;
}> = (props) => {
const { github, organizations } = props;
export const SettingsComponent = () => {
const user = useUser();
const router = useRouter();
const fetch = useFetch();
const load = useCallback(async (path: string) => {
const { github } = await (await fetch('/settings/github')).json();
if (!github) {
return false;
}
const emptyOnes = github.find((p: { login: string }) => !p.login);
const { organizations } = emptyOnes
? await (await fetch(`/settings/organizations/${emptyOnes.id}`)).json()
: { organizations: [] };
return { github, organizations };
}, []);
const { isLoading: isLoadingSettings, data: loadAll } = useSWR(
'load-all',
load
);
useEffect(() => {
if (!isLoadingSettings && !loadAll) {
router.push('/');
}
}, [loadAll, isLoadingSettings]);
if (isLoadingSettings) {
return <LoadingComponent />;
}
if (!loadAll) {
return null;
}
return (
<div className="flex flex-col gap-[68px]">
@ -20,7 +55,10 @@ export const SettingsComponent: FC<{
<div className="text-[#AAA] mt-[4px]">
Connect your GitHub repository to receive updates and analytics
</div>
<GithubComponent github={github} organizations={organizations} />
<GithubComponent
github={loadAll.github}
organizations={loadAll.organizations}
/>
{/*<div className="flex gap-[5px]">*/}
{/* <div>*/}
{/* <Checkbox disableForm={true} checked={true} name="Send Email" />*/}
@ -28,7 +66,7 @@ export const SettingsComponent: FC<{
{/* <div>Show news with everybody in Gitroom</div>*/}
{/*</div>*/}
</div>
{!!user?.tier.team_members && <TeamsComponent />}
{!!user?.tier?.team_members && <TeamsComponent />}
</div>
);
};

View File

@ -84,27 +84,8 @@ export async function middleware(request: NextRequest) {
return redirect;
}
const userResponse = await fetchBackend('/user/self', {
headers: {
auth: authCookie?.value!,
...(showorg?.value ? { showorg: showorg?.value! } : {}),
},
});
return NextResponse.next();
if (userResponse.status === 401) {
return NextResponse.redirect(new URL('/auth/logout', nextUrl.href));
}
if ([200, 201].indexOf(userResponse.status) === -1) {
return NextResponse.redirect(new URL('/err', nextUrl.href));
}
const user = await userResponse.json();
const next = NextResponse.next();
next.headers.set('user', JSON.stringify(user));
return next;
} catch (err) {
return NextResponse.redirect(new URL('/auth/logout', nextUrl.href));
}