Skip to content

Commit

Permalink
Fetch data only from Supabase
Browse files Browse the repository at this point in the history
  • Loading branch information
cdedreuille committed Jul 19, 2024
1 parent 33347a8 commit 074bd72
Show file tree
Hide file tree
Showing 9 changed files with 365 additions and 80 deletions.
38 changes: 27 additions & 11 deletions apps/addon-catalog/app/[...addonName]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { SubHeader } from '@repo/ui';
import { cn } from '@repo/utils';
import { createClient } from '@supabase/supabase-js';
import { fetchAddonDetailsData } from '../../lib/fetch-addon-details-data';
import { cookies } from 'next/headers';
import { createClient as createSupabaseClient } from '../../utils/supabase/server';
import { AddonHero } from '../../components/addon/addon-hero';
import { AddonSidebar } from '../../components/addon/addon-sidebar';
import { Highlight } from '../../components/highlight';
import { type Database } from '../../types/database.types';
import { createMarkdownProcessor } from '../../lib/create-markdown-processor';

interface AddonDetailsProps {
params: {
Expand All @@ -13,34 +15,48 @@ interface AddonDetailsProps {
}

export async function generateStaticParams() {
const supabase = createClient(
const supabase = createClient<Database>(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
);
const { data: addons } = await supabase.from('addons').select();

if (!addons) return [];

// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access -- TODO
return addons.map((addon) => ({ addonName: addon.name?.split('/') }));
}

export default async function AddonDetails({ params }: AddonDetailsProps) {
const cookieStore = cookies();
const supabase = createSupabaseClient(cookieStore);

// TODO: Better decoding?
const name = params.addonName.join('/').replace('%40', '@');
const addon = await fetchAddonDetailsData(name);
// const addon = await fetchAddonDetailsData(name);

const { data: addon } = await supabase
.from('addons')
.select()
.eq('name', name)
.maybeSingle();

if (!addon) return <div>Not found.</div>;

const baseLink = `${addon.repository_url ?? addon.npm_url ?? ''}/`;
const processor = createMarkdownProcessor(baseLink);
const processedReadme = addon.readme
? processor.processSync(addon.readme).toString()
: null;

if (!addon) {
return <div>Not found.</div>;
}
// console.log(processedReadme);

return (
<main className="mb-20">
<SubHeader leftLabel="Back to integrations" leftHref="/" />
<AddonHero addon={addon} />
<div className="flex flex-col gap-12 lg:flex-row">
<div className="flex-1 min-w-0">
{addon.readme ? (
{processedReadme ? (
<Highlight withHTMLChildren={false}>
<div
/**
Expand Down Expand Up @@ -107,12 +123,12 @@ export default async function AddonDetails({ params }: AddonDetailsProps) {
'[&_tr]:border-b',
'[&_tr]:border-b-zinc-200',
)}
dangerouslySetInnerHTML={{ __html: addon.readme }}
dangerouslySetInnerHTML={{ __html: processedReadme }}
/>
</Highlight>
) : null}
</div>
<AddonSidebar addon={addon} />
{/* <AddonSidebar addon={addon} /> */}
</div>
</main>
);
Expand Down
123 changes: 67 additions & 56 deletions apps/addon-catalog/app/api/transfer-catalog-to-supabase/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,70 +12,81 @@ export async function GET() {
// First get all active addons + authors
const { addons, authors } = await fetchMongodbAddons();

const { data: authorsOnSupabase, error: errorAuthors } = await supabase
.from('authors')
.upsert(authors, {
onConflict: 'name',
})
.select();
if (authors) {
const { data: authorsOnSupabase, error: errorAuthors } = await supabase
.from('authors')
.upsert(authors, {
onConflict: 'name',
})
.select();

if (errorAuthors) throw new Error(errorAuthors.message);
if (errorAuthors) throw new Error(errorAuthors.message);

const { data: addonsOnSupabase, error: errorAddons } = await supabase
.from('addons')
.upsert(
addons.map((addon) => ({
name: addon.name,
})),
{
onConflict: 'name',
},
)
.select();
const { data: addonsOnSupabase, error: errorAddons } = await supabase
.from('addons')
.upsert(
addons.map((addon) => ({
name: addon.name,
description: addon.description,
repository_url: addon.repositoryUrl,
homepage_url: addon.homepageUrl,
display_name: addon.displayName,
icon: addon.icon,
verified: addon.verified,
status: addon.status,
weekly_downloads: addon.weeklyDownloads,
readme: addon.readme,
npm_url: addon.npmUrl,
})),
{
onConflict: 'name',
},
)
.select();

if (errorAddons) throw new Error(errorAddons.message);
if (errorAddons) throw new Error(errorAddons.message);

const authorsPerAddon: { author: number; addon: number }[] = addons
.map((addon) =>
addon?.authors
? addon.authors.map((author) => {
const addonId = addonsOnSupabase.find(
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access -- TODO
(a) => a.name === addon.name,
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access -- TODO
)?.id;
const authorId = authorsOnSupabase.find(
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access -- TODO
(a) => a.name === author.name,
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access -- TODO
)?.id;
const authorsPerAddon: { author: number; addon: number; name: string }[] =
addons
.map((addon) =>
addon?.authors
? addon.authors.map((author) => {
const addonId = addonsOnSupabase.find(
(a) => a.name === addon.name,
)?.id;
const authorId = authorsOnSupabase.find(
(a) => a.name === author.name,
)?.id;

return {
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions -- TODO
name: `${addonId}_${authorId}`,
addon: addonId,
author: authorId,
};
})
: [],
)
.flat();
if (!addonId || !authorId) return null;

const { data: addonAuthorOnSupabase, error: errorAddonAuthor } =
await supabase
.from('addon_author')
.upsert(authorsPerAddon, {
onConflict: 'name',
})
.select();
return {
name: `${addonId.toString()}_${authorId.toString()}`,
addon: addonId,
author: authorId,
};
})
: [],
)
.flat()
.filter((a) => a !== null);

if (errorAddonAuthor) throw new Error(errorAddonAuthor.message);
const { data: addonAuthorOnSupabase, error: errorAddonAuthor } =
await supabase
.from('addon_author')
.upsert(authorsPerAddon, {
onConflict: 'name',
})
.select();

return NextResponse.json({
countUpdatedAuthors: authorsOnSupabase.length,
countUpdatedAddons: addonsOnSupabase.length,
countUpdatedAddonAuthors: addonAuthorOnSupabase.length,
});
if (errorAddonAuthor) throw new Error(errorAddonAuthor.message);

return NextResponse.json({
countUpdatedAuthors: authorsOnSupabase.length,
countUpdatedAddons: addonsOnSupabase.length,
countUpdatedAddonAuthors: addonAuthorOnSupabase.length,
});
}
} catch (error) {
if (error instanceof Error) {
return NextResponse.json({ error: error.message });
Expand Down
20 changes: 12 additions & 8 deletions apps/addon-catalog/components/addon/addon-hero.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,17 @@ import copy from 'copy-to-clipboard';
import { useState } from 'react';
import { motion, AnimatePresence } from 'framer-motion';
import { StorybookIcon } from '@repo/ui';
import { type Addon } from '../../types';
import { type Database } from '../../types/database.types';

export function AddonHero({ addon }: { addon: Addon }) {
export function AddonHero({
addon,
}: {
addon: Database['public']['Tables']['addons']['Row'];
}) {
const [state, setState] = useState(false);

const onClick = () => {
copy(`npx install ${addon.name ?? ''}`);
copy(`npx install ${addon?.name ?? ''}`);
setState(true);
setTimeout(() => {
setState(false);
Expand All @@ -27,15 +31,15 @@ export function AddonHero({ addon }: { addon: Addon }) {
return (
<div className="flex justify-between pb-12 mb-12 border-b border-zinc-300 dark:border-b-slate-700">
<div className="flex flex-col gap-8 md:flex-row">
{addon.icon ? (
{addon?.icon ? (
<div
style={{ backgroundImage: `url('${addon.icon}')` }}
style={{ backgroundImage: `url('${addon?.icon}')` }}
className="w-20 h-20 bg-center bg-no-repeat bg-contain"
/>
) : null}
<div className="flex flex-col items-start">
<div className="flex items-center gap-2">
<h1 className="text-2xl font-bold">{addon.displayName}</h1>
<h1 className="text-2xl font-bold">{addon?.display_name}</h1>
{addon.verified &&
['official', 'integrators'].includes(addon.verified) &&
addon.status !== 'deprecated' ? (
Expand Down Expand Up @@ -65,7 +69,7 @@ export function AddonHero({ addon }: { addon: Addon }) {
</AnimatePresence>
</button>
<a
href={addon.repositoryUrl ?? ''}
href={addon.repository_url ?? ''}
target="_blank"
className="flex items-center gap-2 text-sm text-black transition-colors hover:text-blue-500 dark:text-slate-400"
rel="noopener"
Expand All @@ -79,7 +83,7 @@ export function AddonHero({ addon }: { addon: Addon }) {
<div className="flex-col hidden pr-8 lg:flex">
<div className="flex flex-col mb-4">
<div className="text-3xl text-blue-400">
{humanFormat(addon.weeklyDownloads ?? 0, {
{humanFormat(addon.weekly_downloads ?? 0, {
decimals: 0,
separator: '',
})}
Expand Down
8 changes: 6 additions & 2 deletions apps/addon-catalog/components/addon/addon-sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,13 @@ import { BookIcon, EditIcon } from '@storybook/icons';
import Image from 'next/image';
import Link from 'next/link';
import { useState } from 'react';
import { type AddonWithTagLinks } from '../../types';
import { type Database } from '../../types/database.types';

export function AddonSidebar({ addon }: { addon: AddonWithTagLinks }) {
export function AddonSidebar({
addon,
}: {
addon: Database['public']['Tables']['addons']['Row'];
}) {
const [moreAuthorsVisible, setMoreAuthorsVisible] = useState(false);
const authors = addon?.authors ?? [];
const listOfAuthors = moreAuthorsVisible ? authors : authors.slice(0, 6);
Expand Down
12 changes: 11 additions & 1 deletion apps/addon-catalog/lib/fetch-mongodb-addons.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,17 @@ import { database } from './mongodb-client';
interface Addon {
name: string;
disabled?: boolean;
description?: string;
authors: { name: string; email: string }[];
repositoryUrl?: string;
homepageUrl?: string;
displayName?: string;
icon?: string;
verified?: string;
status?: string;
weeklyDownloads?: number;
readme?: string;
npmUrl?: string;
}

async function fetchData(name: string) {
Expand Down Expand Up @@ -51,7 +61,7 @@ export const fetchMongodbAddons = async () => {
(name) => {
return authors.find((a) => a.name === name);
},
);
) as { name: string; email: string }[];

return {
addons: allAddonsWithCurated,
Expand Down
3 changes: 2 additions & 1 deletion apps/addon-catalog/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
"build": "next build",
"start": "next start --port 3001",
"lint": "next lint",
"typecheck": "tsc --noEmit"
"typecheck": "tsc --noEmit",
"gen-types": "supabase gen types typescript --project-id bgfzbtuzejedxudauibw > types/database.types.ts"
},
"dependencies": {
"@docsearch/css": "^3.6.0",
Expand Down
22 changes: 22 additions & 0 deletions apps/addon-catalog/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,28 @@ export interface Addon {
yearlyDownloads?: number | null;
}

export interface AddonSupabase {
name: string;
// compatibility?: Framework[] | null;
description: string | null;
// disabled: boolean | null;
display_name: string | null;
// downloadsAcceleration?: number | null;
homepage_url: string | null;
icon: string | null;
// monthlyDownloads: number | null;
// npmUrl?: string | null;
// published_at: number | null;
// readme: string | null;
repository_url: string | null;
status: Status | null;
// tags?: Tag[] | null;
verified: Verified | null;
// verifiedCreator: string | null;
weekly_downloads: number | null;
// yearlyDownloads: number | null;
}

export interface AddonWithTagLinks extends Omit<Addon, 'tags'> {
tags: TagLinkType[];
}
Expand Down
Loading

0 comments on commit 074bd72

Please sign in to comment.