Skip to content

Commit

Permalink
Feature/RAB-4 history
Browse files Browse the repository at this point in the history
  • Loading branch information
Marek Miklaszewski committed Apr 26, 2023
2 parents b47dbda + 2c0481d commit 9687b43
Show file tree
Hide file tree
Showing 12 changed files with 146 additions and 41 deletions.
2 changes: 1 addition & 1 deletion apps/next-app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
"@types/react": "18.0.37",
"@types/react-dom": "18.0.11",
"autoprefixer": "10.4.14",
"axios": "^1.3.6",
"date-fns": "^2.29.3",
"eslint": "8.38.0",
"eslint-config-next": "13.3.0",
"graphql": "^16.6.0",
Expand Down
1 change: 1 addition & 0 deletions apps/next-app/src/constants/ROUTES.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ export enum ROUTES {
SUBSCRIPTION = '/subscription',
SUBSCRIPTION_SUCCESS = '/subscription/success',
SUBSCRIPTION_CANCEL = '/subscription/cancel',
ANALYTICS = '/analytics',
}
19 changes: 17 additions & 2 deletions apps/next-app/src/middleware.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import { createMiddlewareSupabaseClient } from '@supabase/auth-helpers-nextjs';
import { ROUTES } from 'constants/ROUTES';
import { NextRequest, NextResponse } from 'next/server';
import { GET_PROFILE } from 'shared/queries/index.graphql';
import { getApolloServerClient } from 'shared/services/apollo';

export async function middleware(req: NextRequest) {
const res = NextResponse.next();
Expand All @@ -10,7 +13,19 @@ export async function middleware(req: NextRequest) {
} = await supabase.auth.getSession();

if (session) {
// Authentication successful, forward request to protected route.
if (req.nextUrl.pathname.startsWith(ROUTES.ANALYTICS)) {
const client = getApolloServerClient(session.access_token);

const { data } = await client.query({
query: GET_PROFILE,
variables: { profileId: session.user.id },
});
if (!data.profilesCollection?.edges[0].node.subscription) {
const redirectUrl = req.nextUrl.clone();
redirectUrl.pathname = ROUTES.SUBSCRIPTION;
return NextResponse.redirect(redirectUrl);
}
}
return res;
}

Expand All @@ -21,5 +36,5 @@ export async function middleware(req: NextRequest) {
}

export const config = {
matcher: ['/dashboard', '/profile', '/subscription'],
matcher: ['/dashboard', '/profile', '/subscription', '/analytics'],
};
21 changes: 14 additions & 7 deletions apps/next-app/src/pages/_app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,22 @@ import { Nunito } from 'next/font/google';
import type { AppProps } from 'next/app';
import { AppProviders } from 'providers/AppProviders';
import { SessionContextProvider, Session } from '@supabase/auth-helpers-react';
import { useState } from 'react';
import { ReactElement, ReactNode, useState } from 'react';
import { createBrowserSupabaseClient } from '@supabase/auth-helpers-nextjs';
import { NextPage } from 'next';
import { Layout } from 'shared/components/Layout';

const nunito = Nunito({ subsets: ['latin'], weight: ['300', '500', '700'] });

export default function App({
Component,
pageProps,
}: AppProps<{
export type NextPageWithLayout<P = {}, IP = P> = NextPage<P, IP> & {
getLayout?: (page: ReactElement) => ReactNode;
};

type CustomAppProps = AppProps & {
initialSession: Session;
}>) {
};

export default function App({ Component, pageProps }: CustomAppProps) {
const [supabaseClient] = useState(() => createBrowserSupabaseClient());

return (
Expand All @@ -23,7 +28,9 @@ export default function App({
>
<AppProviders>
<main className={nunito.className}>
<Component {...pageProps} />
<Layout>
<Component {...pageProps} />)
</Layout>
</main>
</AppProviders>
</SessionContextProvider>
Expand Down
9 changes: 9 additions & 0 deletions apps/next-app/src/pages/analytics/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
const Analytics = () => {
return (
<div className="flex min-h-screen flex-col items-center justify-center p-24">
<h1 className="text-4xl">Hello Analytics!</h1>
</div>
);
};

export default Analytics;
12 changes: 10 additions & 2 deletions apps/next-app/src/pages/profile/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,15 @@ import { GetServerSidePropsContext } from 'next';
import { AvatarChanger } from 'shared/components/profile/AvatarChanger';
import { EditProfileForm } from 'shared/components/profile/EditProfileForm';
import { getApolloServerClient } from 'shared/services/apollo';
import { EDGE_FUNCTION_NAMES } from 'constants/EDGE_FUNCTION_NAMES';
import { TransactionHistory } from 'shared/components/subscriptions/TransactionHistory';

interface ProfileProps {
profile: GetProfileQuery['profilesCollection'];
charges: any;
}

const Profile = ({ profile }: ProfileProps) => {
const Profile = ({ profile, charges }: ProfileProps) => {
const userProfile = profile?.edges[0];

return (
Expand All @@ -19,6 +22,7 @@ const Profile = ({ profile }: ProfileProps) => {

<AvatarChanger profileAvatarSrc={userProfile?.node.avatar_url} />
<EditProfileForm fullName={userProfile?.node.full_name!} />
<TransactionHistory transactions={charges} />
</div>
);
};
Expand All @@ -34,5 +38,9 @@ export const getServerSideProps = async (ctx: GetServerSidePropsContext) => {
query: GET_PROFILE,
variables: { profileId: session.data.session?.user.id },
});
return { props: { profile: data.profilesCollection } };

const { data: chargesData } = await supabase.functions.invoke(
EDGE_FUNCTION_NAMES.GET_STRIPE_CHARGES
);
return { props: { profile: data.profilesCollection, charges: chargesData } };
};
Original file line number Diff line number Diff line change
@@ -1,7 +1,15 @@
import CrossmarkSVG from 'assets/cross-mark.svg';

export const Crossmark = () => {
interface CrossmarkProps {
isError?: boolean;
}

export const Crossmark = ({ isError }: CrossmarkProps) => {
return (
<CrossmarkSVG className="w-4 h-4 mr-1.5 text-gray-400 flex-shrink-0" />
<CrossmarkSVG
className={`w-4 h-4 mr-1.5 ${
isError ? 'text-red-400' : 'text-gray-400'
} flex-shrink-0`}
/>
);
};
1 change: 1 addition & 0 deletions apps/next-app/src/shared/components/Layout/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { Layout } from './layout.component';
29 changes: 29 additions & 0 deletions apps/next-app/src/shared/components/Layout/layout.component.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { ROUTES } from 'constants/ROUTES';
import Link from 'next/link';
import { ReactNode } from 'react';

interface LayoutProps {
children: ReactNode;
}

export const Layout = ({ children }: LayoutProps) => {
return (
<div>
<header className="flex gap-8 py-8 px-8">
<Link href={ROUTES.PROFILE} className="text-white">
Profile
</Link>
<Link href={ROUTES.DASHBOARD} className="text-white">
Dashboard
</Link>
<Link href={ROUTES.SUBSCRIPTION} className="text-white">
Subscription
</Link>
<Link href={ROUTES.ANALYTICS} className="text-white">
Analytics
</Link>
</header>
{children}
</div>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { TransactionHistory } from './transactionHistory.component';
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { format, fromUnixTime } from 'date-fns';
import { Checkmark } from 'shared/components/Checkmark';
import { Crossmark } from 'shared/components/Crossmark';
import { getSubscriptionPrice } from 'utils/getSubscriptionPrice';

interface TransactionHistoryProps {
transactions: any;
}

export const TransactionHistory = ({
transactions,
}: TransactionHistoryProps) => {
return (
<div className="max-w-xl w-full">
<h2 className="mt-8 text-2xl">Transaction history</h2>
<div className="flex flex-col">
{transactions.charges.data.map(
({ id, created, description, amount, currency, status }: any) => (
<div
key={id}
className="flex justify-between w-full border-b-2 border-slate-800 py-6"
>
<div>
{description}{' '}
<span className="text-slate-400">
({format(fromUnixTime(created), 'Pp')})
</span>
</div>
<div>
{getSubscriptionPrice(amount)} {currency.toUpperCase()}
</div>
{status === 'succeeded' ? <Checkmark /> : <Crossmark isError />}
</div>
)
)}
</div>
</div>
);
};
41 changes: 14 additions & 27 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 9687b43

Please sign in to comment.