Skip to content

Commit

Permalink
feat: add new component detail and preview (#30)
Browse files Browse the repository at this point in the history
  • Loading branch information
lopesdasilva authored Feb 27, 2024
1 parent 3a89bdb commit 18f0e5e
Show file tree
Hide file tree
Showing 58 changed files with 2,244 additions and 925 deletions.
1,430 changes: 1,331 additions & 99 deletions ethereal-nexus-dashboard/package-lock.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions ethereal-nexus-dashboard/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
"react": "18.2.0",
"react-dom": "18.2.0",
"react-hook-form": "^7.46.1",
"react-markdown": "^9.0.1",
"readable-stream": "^4.5.2",
"swagger-ui-react": "^5.1.3",
"tailwind-merge": "^1.14.0",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import {getComponentAssets, getComponentById, getComponentVersions} from "@/data/components/actions";
import PreviewContainer from "@/components/components/component/version/preview-container";
import {notFound} from "next/navigation";

export const revalidate = 0;
export default async function PreviewVersion({params: {id, versionId}}: any) {


const component = await getComponentById(id);
const versions = await getComponentVersions(id);
const componentAssets = await getComponentAssets(id, versionId);
if (!versions.success || !component.success || !componentAssets.success) {
notFound();
}
const selectedVersion = versions.data.filter((version: any) => version.id === versionId)[0];


return (<PreviewContainer
component={component}
selectedVersion={selectedVersion}
key={versionId}
componentSlug={component.data.slug}
componentAssets={componentAssets.data}/>);
}
16 changes: 16 additions & 0 deletions ethereal-nexus-dashboard/src/app/(iframe)/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
export const metadata = {
title: 'Next.js',
description: 'Generated by Next.js',
}

export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="en">
<body>{children}</body>
</html>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import {notFound, redirect} from 'next/navigation';
import {getComponentVersions} from "@/data/components/actions";


export default async function EditComponent({params: {id}}: any) {

const versions = await getComponentVersions(id);
if (!versions.success) {
notFound();
}
redirect(`/components/${id}/versions/${versions.data[0].id}`);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import {Separator} from '@/components/ui/separator';
import {notFound} from 'next/navigation';
import {getComponentAssets, getComponentById, getComponentVersions} from "@/data/components/actions";
import React from "react";
import ComponentVersionHeader from "@/components/components/component/version/header";
import ComponentVersionTabs from "@/components/components/component/version/tabs";


export default async function EditComponentVersion({params: {id, versionId, tab}}: any) {

const component = await getComponentById(id);
const versions = await getComponentVersions(id);

if (!versions.success || !component.success) {
notFound();
}
const selectedVersion = versions.data.filter((version: any) => version.id === versionId)[0];
return (
<div className="container space-y-6">
<ComponentVersionHeader versions={versions} component={component}
selectedVersion={selectedVersion} activeTab={tab}/>
<Separator/>
<ComponentVersionTabs activeTab={tab} versions={versions} selectedVersion={selectedVersion}
component={component}/>
</div>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import {redirect} from 'next/navigation';


export default async function EditComponentVersion({params: {id, versionId}}: any) {
redirect(`/components/${id}/versions/${versionId}/readme`);
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import "swagger-ui-react/swagger-ui.css";
import { getSwaggerSpec } from "@/lib/api-doc/api-doc.service";
import ReactSwagger from "@/app/api-doc/react-swagger";
import ReactSwagger from "./react-swagger";

export default async function ApiDoc() {
const spec = await getSwaggerSpec();
Expand Down
12 changes: 12 additions & 0 deletions ethereal-nexus-dashboard/src/app/(layout)/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import React from "react";
import DashboardLayout from "@/components/layout";

export default async function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
const env = process.env.NODE_ENV

return <DashboardLayout>{children}</DashboardLayout>;
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { logger } from "@/logger"; // our logger import
export default async function Home() {
const session = await auth()
const projects = await getProjects(session?.user?.id);
logger.info("Home Page called "); // calling our logger


return <div className="container flex-col md:flex">
<div className="flex-1 space-y-4 p-8 pt-6">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ export const POST = authenticatedWithKey(
const { request: responseFromBlob = {} as any } = _response;
const { url } = responseFromBlob;

console.log('POST assets', JSON.stringify(_response, undefined, 2))
// console.log('POST assets', JSON.stringify(_response, undefined, 2))
if (!url) {
return NextResponse.json('Failed to upload assets', {
status: HttpStatus.BAD_REQUEST,
Expand Down
5 changes: 3 additions & 2 deletions ethereal-nexus-dashboard/src/app/api/v1/components/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@ import { NextRequest, NextResponse } from 'next/server';
import { HttpStatus } from '@/app/api/utils';
import { authenticatedWithKey } from '@/lib/route-wrappers';
import { upsertComponent } from '@/data/components/actions';
import {revalidatePath} from "next/cache";

/**
* @swagger
* /api/v1/components:
* post:
* summary: Creates/Updates a project
* description: Creates/Updates a project
* summary: Creates/Updates a component
* description: Creates/Updates a component
* tags:
* - Components
* produces:
Expand Down
4 changes: 2 additions & 2 deletions ethereal-nexus-dashboard/src/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import React from "react";
import { Inter } from "next/font/google";
import DashboardLayout from "@/components/layout";
import { ThemeProvider } from '@/components/theme-provider';
import NewRelicSnippet from '../components/newrelicSnippet'
import NewRelicSnippet from '@/components/newrelicSnippet'

const inter = Inter({ subsets: ["latin"] });

Expand All @@ -21,7 +21,7 @@ export default async function RootLayout({
</head>): null}
<body className={inter.className}>
<ThemeProvider attribute="class" defaultTheme="system" enableSystem>
<DashboardLayout>{children}</DashboardLayout>
{children}
</ThemeProvider>
</body>
</html>;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
"use client";

import React from 'react';
import {
Select,
SelectContent,
SelectGroup,
SelectItem,
SelectLabel,
SelectTrigger,
SelectValue
} from "@/components/ui/select";
import {useRouter} from "next/navigation";


const ComponentVersionHeader = ({component,selectedVersion,versions,activeTab}) => {
const { push } = useRouter();

return (
<div className={"flex"}>
<div>
<h3 className="text-lg font-medium">{component.data.name}</h3>
<p className="text-sm text-muted-foreground">
{selectedVersion.version} ~ {component.data.slug} - {selectedVersion.created_at.toLocaleString('en-US', { weekday: 'short', year: 'numeric', month: 'long', day: 'numeric', hour: '2-digit', minute: '2-digit' })}
</p>
</div>
<div className="ml-auto">
<Select value={selectedVersion.id} onValueChange={(newVersionId) => push(`/components/${component.data.id}/versions/${newVersionId}/${activeTab}`)}>
<SelectTrigger className="w-[180px]">
<SelectValue placeholder="Select a version"/>
</SelectTrigger>
<SelectContent>
<SelectGroup>
<SelectLabel>Version</SelectLabel>
{versions.data.map((version) => (
<SelectItem key={version.id} value={version.id}>{version.version}</SelectItem>))}
</SelectGroup>
</SelectContent>
</Select>
</div>
</div>
);
};

export default ComponentVersionHeader;
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
"use client"
import React, {useState} from 'react';
import Script from "next/script";
import {useRouter} from "next/navigation";
import {Table, TableBody, TableCell, TableHead, TableHeader, TableRow} from "@/components/ui/table";
import {Input} from "@/components/ui/input";
import Link from "next/link";
import {ExternalLinkIcon} from "@radix-ui/react-icons";
import {z} from "zod";
import {componentAssetsSchema} from "@/data/components/dto";


interface PreviewContainerProps {
componentSlug: string | null;
componentAssets: z.infer<typeof componentAssetsSchema>[];
selectedVersion: any;
component: any;
}

const PreviewContainer: React.FC<PreviewContainerProps> = ({
component,
componentAssets = [],
componentSlug,
selectedVersion
}) => {
const [inputValues, setInputValues] = useState({});

const router = useRouter();
const mappedObjStr = Object.entries(inputValues)
.map(([key, value]) => `${key}="${value}"`)
.join(' ');

return (
<div>
<div
className={"mt-4 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 relative rounded-md border"}
style={{position: 'relative'}}>
<div style={{position: 'absolute', top: '2px', right: '2px'}}>
<Link target={'_blank'}
href={`/components/${component.data.id}/versions/${selectedVersion.id}/preview-new-window`}>
<button className="m-4 p-2 border rounded">

<ExternalLinkIcon/>
</button>
</Link>
</div>
<div className="flex items-center justify-between p-4 min-h-[300px]">
{componentAssets.map(({url, type, id}, index) => {
if (type === "js") {
return <Script key={id} src={url} type={"module"}></Script>
}

})}
<div key={selectedVersion}
id={selectedVersion}
dangerouslySetInnerHTML={{
__html: `
<${componentSlug} ${mappedObjStr}></${componentSlug}>`
}}
/>
</div>
</div>
<Table className="mt-4">
<TableHeader>
<TableRow>
<TableHead className="w-[100px]">Name</TableHead>
<TableHead>Description</TableHead>
<TableHead>Default</TableHead>
<TableHead>Type</TableHead>
<TableHead>Controls</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{selectedVersion.dialog.map((item: any, index: number) => (
<TableRow key={index}>
<TableCell className="font-medium">{item.name}</TableCell>
<TableCell>{item.placeholder}</TableCell>
<TableCell>{item.type}</TableCell>
<TableCell>
<code
className="relative rounded bg-muted px-[0.3rem] py-[0.2rem] font-mono text-sm font-semibold">
{item.default ? item.default : "undefined"}
</code>
</TableCell>
<TableCell className="text-right">
<Input onChange={(e) => setInputValues({...inputValues, [item.name]: e.target.value})}
placeholder="Value"/></TableCell>
</TableRow>
))}

</TableBody>
</Table>
</div>
);
};

export default PreviewContainer;
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
"use client";
import React from 'react';
import {Tabs, TabsContent, TabsList, TabsTrigger} from "@/components/ui/tabs";
import Preview from "@/components/components/component/version/tabs/preview";
import {useRouter} from "next/navigation";
import Readme from "@/components/components/component/version/tabs/readme";
import Dependents from "@/components/components/component/version/tabs/dependents";


const ComponentVersionTabs = ({activeTab, component, versions, selectedVersion}) => {
const {push} = useRouter();

return (
<Tabs defaultValue={activeTab}
onValueChange={(tab) => push(`/components/${component.data.id}/versions/${selectedVersion.id}/${tab}`)}>
<TabsList className={"mb-6"}>
<TabsTrigger value="readme">Readme</TabsTrigger>
<TabsTrigger value="preview">Preview</TabsTrigger>
<TabsTrigger value="dependents">Dependents</TabsTrigger>
<TabsTrigger value="versions">Versions</TabsTrigger>
</TabsList>
<TabsContent value="readme">
<Readme selectedVersion={selectedVersion}/>
</TabsContent>
<TabsContent value="preview">
<Preview key={selectedVersion.id} componentId={component.data.id}
selectedVersionId={selectedVersion.id}/>
</TabsContent>
<TabsContent value="dependents">
<Dependents></Dependents>
</TabsContent>
<TabsContent value="versions">
<ul role="list" className="divide-y divide-gray-100">
{versions.data.map((version: any, index) => (
<li key={index} className="flex justify-between gap-x-6 py-5">
<div className="flex min-w-0 gap-x-4">
<div className="min-w-0 flex-auto">
<p className="text-sm font-semibold leading-6 text-gray-900">
{version.version}</p>
<p className="mt-1 truncate text-xs leading-5 text-gray-500">60,000
Downloads</p>
</div>
</div>
<div className="hidden shrink-0 sm:flex sm:flex-col sm:items-end">
<p className="text-sm leading-6 text-gray-900">Published</p>
<p className="mt-1 text-xs leading-5 text-gray-500">
<time
dateTime={version.created_at}>{version.created_at.toLocaleString('en-US', {
weekday: 'short',
year: 'numeric',
month: 'long',
day: 'numeric',
hour: '2-digit',
minute: '2-digit'
})}</time>
</p>
</div>
</li>))}
</ul>
</TabsContent>
</Tabs>
);
};

export default ComponentVersionTabs;
Loading

0 comments on commit 18f0e5e

Please sign in to comment.