mirror of
https://litchi.icu/ngc2207/mine-code-now.git
synced 2025-07-06 20:21:08 +00:00
feat: add snippet-card and snippets-page
feat: add Hello World snippets for additional languages in seed.ts
This commit is contained in:
parent
1196e50602
commit
a284915c46
@ -13,11 +13,14 @@
|
||||
"@radix-ui/react-collapsible": "^1.1.1",
|
||||
"@radix-ui/react-dialog": "^1.1.2",
|
||||
"@radix-ui/react-dropdown-menu": "^2.1.2",
|
||||
"@radix-ui/react-scroll-area": "^1.2.1",
|
||||
"@radix-ui/react-separator": "^1.1.0",
|
||||
"@radix-ui/react-slot": "^1.1.0",
|
||||
"@radix-ui/react-tabs": "^1.1.1",
|
||||
"@radix-ui/react-tooltip": "^1.1.4",
|
||||
"class-variance-authority": "^0.7.0",
|
||||
"clsx": "^2.1.1",
|
||||
"devicons-react": "^1.3.0",
|
||||
"lucide-react": "^0.460.0",
|
||||
"next": "15.0.3",
|
||||
"next-themes": "^0.4.3",
|
||||
|
102
prisma/seed.ts
102
prisma/seed.ts
@ -5,7 +5,7 @@ async function main() {
|
||||
data: [
|
||||
{
|
||||
title: "Hello World in JavaScript",
|
||||
language: "JavaScript",
|
||||
language: "javascript",
|
||||
code: `console.log("Hello, World!");
|
||||
|
||||
function greet(name) {
|
||||
@ -16,7 +16,7 @@ console.log(greet("Prisma"));`,
|
||||
},
|
||||
{
|
||||
title: "Hello World in Python",
|
||||
language: "Python",
|
||||
language: "python",
|
||||
code: `print("Hello, World!")
|
||||
|
||||
def greet(name):
|
||||
@ -26,7 +26,7 @@ print(greet("Prisma"))`,
|
||||
},
|
||||
{
|
||||
title: "Hello World in Java",
|
||||
language: "Java",
|
||||
language: "java",
|
||||
code: `public class HelloWorld {
|
||||
public static void main(String[] args) {
|
||||
System.out.println("Hello, World!");
|
||||
@ -39,6 +39,102 @@ print(greet("Prisma"))`,
|
||||
public static void main(String[] args) {
|
||||
System.out.println(greet("Prisma"));
|
||||
}
|
||||
}`,
|
||||
},
|
||||
{
|
||||
title: "Hello World in C",
|
||||
language: "c",
|
||||
code: `#include <stdio.h>
|
||||
|
||||
void greet(char* name) {
|
||||
printf("Hello, %s!\n", name);
|
||||
}
|
||||
|
||||
int main() {
|
||||
printf("Hello, World!\n");
|
||||
greet("Prisma");
|
||||
return 0;
|
||||
}`,
|
||||
},
|
||||
{
|
||||
title: "Hello World in C++",
|
||||
language: "cpp",
|
||||
code: `#include <iostream>
|
||||
|
||||
void greet(std::string name) {
|
||||
std::cout << "Hello, " << name << "!" << std::endl;
|
||||
}
|
||||
|
||||
int main() {
|
||||
std::cout << "Hello, World!" << std::endl;
|
||||
greet("Prisma");
|
||||
return 0;
|
||||
}`,
|
||||
},
|
||||
{
|
||||
title: "Hello World in C#",
|
||||
language: "csharp",
|
||||
code: `using System;
|
||||
|
||||
class HelloWorld {
|
||||
static void Main() {
|
||||
Console.WriteLine("Hello, World!");
|
||||
greet("Prisma");
|
||||
}
|
||||
|
||||
static void greet(string name) {
|
||||
Console.WriteLine("Hello, " + name + "!");
|
||||
}
|
||||
}`,
|
||||
},
|
||||
{
|
||||
title: "Hello World in Go",
|
||||
language: "go",
|
||||
code: `package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func greet(name string) {
|
||||
fmt.Printf("Hello, %s!\n", name)
|
||||
}
|
||||
|
||||
func main() {
|
||||
fmt.Println("Hello, World!")
|
||||
greet("Prisma")
|
||||
}`,
|
||||
},
|
||||
{
|
||||
title: "Hello World in Ruby",
|
||||
language: "ruby",
|
||||
code: `puts "Hello, World!"
|
||||
|
||||
def greet(name)
|
||||
puts "Hello, #{name}!"
|
||||
end
|
||||
|
||||
greet("Prisma")`,
|
||||
},
|
||||
{
|
||||
title: "Hello World in Swift",
|
||||
language: "swift",
|
||||
code: `print("Hello, World!")
|
||||
|
||||
func greet(_ name: String) {
|
||||
print("Hello, \(name)!")
|
||||
}
|
||||
|
||||
greet("Prisma")`,
|
||||
},
|
||||
{
|
||||
title: "Hello World in Kotlin",
|
||||
language: "kotlin",
|
||||
code: `fun main() {
|
||||
println("Hello, World!")
|
||||
greet("Prisma")
|
||||
}
|
||||
|
||||
fun greet(name: String) {
|
||||
println("Hello, $name!")
|
||||
}`,
|
||||
},
|
||||
],
|
||||
|
@ -19,14 +19,14 @@ export default async function RootLayout({
|
||||
<SidebarProvider defaultOpen={defaultOpen}>
|
||||
<AppSidebar />
|
||||
<SidebarInset>
|
||||
<header className="flex h-16 shrink-0 items-center gap-2">
|
||||
<header className="flex h-12 shrink-0 items-center gap-2">
|
||||
<div className="flex items-center gap-2 px-4">
|
||||
<SidebarTrigger className="-ml-1" />
|
||||
<Separator orientation="vertical" className="mr-2 h-4" />
|
||||
<ThemeToggle />
|
||||
</div>
|
||||
</header>
|
||||
{children}
|
||||
<div className="h-full rounded-b-xl">{children}</div>
|
||||
</SidebarInset>
|
||||
</SidebarProvider>
|
||||
);
|
||||
|
106
src/app/dashboard/snippets/components/snippet-card.tsx
Normal file
106
src/app/dashboard/snippets/components/snippet-card.tsx
Normal file
@ -0,0 +1,106 @@
|
||||
import {
|
||||
CsharpOriginal,
|
||||
JavaOriginal,
|
||||
PythonOriginal,
|
||||
JavascriptOriginal,
|
||||
KotlinOriginal,
|
||||
COriginal,
|
||||
GoOriginal,
|
||||
RubyOriginal,
|
||||
SwiftOriginal,
|
||||
CplusplusOriginal,
|
||||
} from "devicons-react";
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardFooter,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from "@/components/ui/card";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { Snippet } from "@prisma/client";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { ScrollArea } from "@/components/ui/scroll-area";
|
||||
|
||||
const colors = {
|
||||
green: "bg-green-50",
|
||||
gray: "bg-gray-50",
|
||||
red: "bg-red-50",
|
||||
yellow: "bg-yellow-50",
|
||||
purple: "bg-purple-50",
|
||||
blue: "bg-blue-50",
|
||||
orange: "bg-orange-50",
|
||||
} as const;
|
||||
|
||||
const languageIcons = {
|
||||
c: { icon: COriginal, color: colors.green },
|
||||
cpp: { icon: CplusplusOriginal, color: colors.gray },
|
||||
java: { icon: JavaOriginal, color: colors.red },
|
||||
python: { icon: PythonOriginal, color: colors.yellow },
|
||||
javascript: { icon: JavascriptOriginal, color: colors.yellow },
|
||||
csharp: { icon: CsharpOriginal, color: colors.purple },
|
||||
go: { icon: GoOriginal, color: colors.blue },
|
||||
ruby: { icon: RubyOriginal, color: colors.red },
|
||||
swift: { icon: SwiftOriginal, color: colors.orange },
|
||||
kotlin: { icon: KotlinOriginal, color: colors.blue },
|
||||
} as const;
|
||||
|
||||
type Language = keyof typeof languageIcons;
|
||||
|
||||
interface SnippetCardProps extends React.ComponentProps<typeof Card> {
|
||||
snippet: Snippet;
|
||||
}
|
||||
|
||||
export function SnippetCard({
|
||||
snippet,
|
||||
className,
|
||||
...props
|
||||
}: SnippetCardProps) {
|
||||
const languageInfo = languageIcons[snippet.language as Language];
|
||||
const createdAt = new Date(snippet.createdAt);
|
||||
const updatedAt = new Date(snippet.updatedAt);
|
||||
const isUpdated = createdAt.getTime() !== updatedAt.getTime();
|
||||
|
||||
const renderLanguageBadge = () => (
|
||||
<Badge
|
||||
variant="outline"
|
||||
className={cn(
|
||||
languageInfo?.color,
|
||||
"rounded-xl flex items-center justify-between whitespace-nowrap h-6"
|
||||
)}
|
||||
>
|
||||
<div className="flex items-center gap-1">
|
||||
{languageInfo?.icon && <languageInfo.icon />}
|
||||
<span>{snippet.language}</span>
|
||||
</div>
|
||||
</Badge>
|
||||
);
|
||||
|
||||
const renderTimestamp = () => (
|
||||
<span className="text-sm text-muted-foreground">
|
||||
{isUpdated ? "updatedAt: " : "createdAt: "}
|
||||
{isUpdated ? updatedAt.toLocaleString() : createdAt.toLocaleString()}
|
||||
</span>
|
||||
);
|
||||
|
||||
return (
|
||||
<Card className={cn("flex flex-col", className)} {...props}>
|
||||
<CardHeader className="flex flex-col flex-none p-0">
|
||||
<CardTitle className="h-8 p-2 pl-4 flex items-center justify-between bg-muted rounded-t-xl">
|
||||
<span className="truncate">{snippet.title}</span>
|
||||
{renderLanguageBadge()}
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="flex-grow overflow-hidden p-0">
|
||||
<ScrollArea className="h-full border-y">
|
||||
<pre className="p-2 rounded-md overflow-auto whitespace-pre">
|
||||
<code>{snippet.code}</code>
|
||||
</pre>
|
||||
</ScrollArea>
|
||||
</CardContent>
|
||||
<CardFooter className="flex-none h-6 p-2 pl-4 mb-0 bg-muted rounded-b-xl">
|
||||
{renderTimestamp()}
|
||||
</CardFooter>
|
||||
</Card>
|
||||
);
|
||||
}
|
35
src/app/dashboard/snippets/page.tsx
Normal file
35
src/app/dashboard/snippets/page.tsx
Normal file
@ -0,0 +1,35 @@
|
||||
import prisma from "@/lib/prisma";
|
||||
import { PlusCircle } from "lucide-react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Separator } from "@/components/ui/separator";
|
||||
import { SnippetCard } from "./components/snippet-card";
|
||||
|
||||
export default async function SnippetsPage() {
|
||||
const snippets = await prisma.snippet.findMany();
|
||||
const renderedSnippets = snippets.map((snippet) => {
|
||||
return <SnippetCard key={snippet.id} snippet={snippet} />;
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="h-full px-4 py-2 lg:py-4 lg:px-8">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="space-y-1">
|
||||
<h2 className="text-2xl font-semibold tracking-tight">Snippets</h2>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Top picks for you. Updated daily.
|
||||
</p>
|
||||
</div>
|
||||
<div className="ml-auto mr-4">
|
||||
<Button>
|
||||
<PlusCircle />
|
||||
Share Snippet
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<Separator className="my-4" />
|
||||
<div className="grid sm:grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4">
|
||||
{renderedSnippets}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
36
src/components/ui/badge.tsx
Normal file
36
src/components/ui/badge.tsx
Normal file
@ -0,0 +1,36 @@
|
||||
import * as React from "react"
|
||||
import { cva, type VariantProps } from "class-variance-authority"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
const badgeVariants = cva(
|
||||
"inline-flex items-center rounded-md border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default:
|
||||
"border-transparent bg-primary text-primary-foreground shadow hover:bg-primary/80",
|
||||
secondary:
|
||||
"border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
||||
destructive:
|
||||
"border-transparent bg-destructive text-destructive-foreground shadow hover:bg-destructive/80",
|
||||
outline: "text-foreground",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: "default",
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
export interface BadgeProps
|
||||
extends React.HTMLAttributes<HTMLDivElement>,
|
||||
VariantProps<typeof badgeVariants> {}
|
||||
|
||||
function Badge({ className, variant, ...props }: BadgeProps) {
|
||||
return (
|
||||
<div className={cn(badgeVariants({ variant }), className)} {...props} />
|
||||
)
|
||||
}
|
||||
|
||||
export { Badge, badgeVariants }
|
76
src/components/ui/card.tsx
Normal file
76
src/components/ui/card.tsx
Normal file
@ -0,0 +1,76 @@
|
||||
import * as React from "react"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
const Card = React.forwardRef<
|
||||
HTMLDivElement,
|
||||
React.HTMLAttributes<HTMLDivElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<div
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"rounded-xl border bg-card text-card-foreground shadow",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
Card.displayName = "Card"
|
||||
|
||||
const CardHeader = React.forwardRef<
|
||||
HTMLDivElement,
|
||||
React.HTMLAttributes<HTMLDivElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<div
|
||||
ref={ref}
|
||||
className={cn("flex flex-col space-y-1.5 p-6", className)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
CardHeader.displayName = "CardHeader"
|
||||
|
||||
const CardTitle = React.forwardRef<
|
||||
HTMLDivElement,
|
||||
React.HTMLAttributes<HTMLDivElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<div
|
||||
ref={ref}
|
||||
className={cn("font-semibold leading-none tracking-tight", className)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
CardTitle.displayName = "CardTitle"
|
||||
|
||||
const CardDescription = React.forwardRef<
|
||||
HTMLDivElement,
|
||||
React.HTMLAttributes<HTMLDivElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<div
|
||||
ref={ref}
|
||||
className={cn("text-sm text-muted-foreground", className)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
CardDescription.displayName = "CardDescription"
|
||||
|
||||
const CardContent = React.forwardRef<
|
||||
HTMLDivElement,
|
||||
React.HTMLAttributes<HTMLDivElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<div ref={ref} className={cn("p-6 pt-0", className)} {...props} />
|
||||
))
|
||||
CardContent.displayName = "CardContent"
|
||||
|
||||
const CardFooter = React.forwardRef<
|
||||
HTMLDivElement,
|
||||
React.HTMLAttributes<HTMLDivElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<div
|
||||
ref={ref}
|
||||
className={cn("flex items-center p-6 pt-0", className)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
CardFooter.displayName = "CardFooter"
|
||||
|
||||
export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent }
|
48
src/components/ui/scroll-area.tsx
Normal file
48
src/components/ui/scroll-area.tsx
Normal file
@ -0,0 +1,48 @@
|
||||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
const ScrollArea = React.forwardRef<
|
||||
React.ElementRef<typeof ScrollAreaPrimitive.Root>,
|
||||
React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.Root>
|
||||
>(({ className, children, ...props }, ref) => (
|
||||
<ScrollAreaPrimitive.Root
|
||||
ref={ref}
|
||||
className={cn("relative overflow-hidden", className)}
|
||||
{...props}
|
||||
>
|
||||
<ScrollAreaPrimitive.Viewport className="h-full w-full rounded-[inherit]">
|
||||
{children}
|
||||
</ScrollAreaPrimitive.Viewport>
|
||||
<ScrollBar />
|
||||
<ScrollAreaPrimitive.Corner />
|
||||
</ScrollAreaPrimitive.Root>
|
||||
))
|
||||
ScrollArea.displayName = ScrollAreaPrimitive.Root.displayName
|
||||
|
||||
const ScrollBar = React.forwardRef<
|
||||
React.ElementRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>,
|
||||
React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>
|
||||
>(({ className, orientation = "vertical", ...props }, ref) => (
|
||||
<ScrollAreaPrimitive.ScrollAreaScrollbar
|
||||
ref={ref}
|
||||
orientation={orientation}
|
||||
className={cn(
|
||||
"flex touch-none select-none transition-colors",
|
||||
orientation === "vertical" &&
|
||||
"h-full w-2.5 border-l border-l-transparent p-[1px]",
|
||||
orientation === "horizontal" &&
|
||||
"h-2.5 flex-col border-t border-t-transparent p-[1px]",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<ScrollAreaPrimitive.ScrollAreaThumb className="relative flex-1 rounded-full bg-border" />
|
||||
</ScrollAreaPrimitive.ScrollAreaScrollbar>
|
||||
))
|
||||
ScrollBar.displayName = ScrollAreaPrimitive.ScrollAreaScrollbar.displayName
|
||||
|
||||
export { ScrollArea, ScrollBar }
|
55
src/components/ui/tabs.tsx
Normal file
55
src/components/ui/tabs.tsx
Normal file
@ -0,0 +1,55 @@
|
||||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import * as TabsPrimitive from "@radix-ui/react-tabs"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
const Tabs = TabsPrimitive.Root
|
||||
|
||||
const TabsList = React.forwardRef<
|
||||
React.ElementRef<typeof TabsPrimitive.List>,
|
||||
React.ComponentPropsWithoutRef<typeof TabsPrimitive.List>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<TabsPrimitive.List
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"inline-flex h-9 items-center justify-center rounded-lg bg-muted p-1 text-muted-foreground",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
TabsList.displayName = TabsPrimitive.List.displayName
|
||||
|
||||
const TabsTrigger = React.forwardRef<
|
||||
React.ElementRef<typeof TabsPrimitive.Trigger>,
|
||||
React.ComponentPropsWithoutRef<typeof TabsPrimitive.Trigger>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<TabsPrimitive.Trigger
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"inline-flex items-center justify-center whitespace-nowrap rounded-md px-3 py-1 text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
TabsTrigger.displayName = TabsPrimitive.Trigger.displayName
|
||||
|
||||
const TabsContent = React.forwardRef<
|
||||
React.ElementRef<typeof TabsPrimitive.Content>,
|
||||
React.ComponentPropsWithoutRef<typeof TabsPrimitive.Content>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<TabsPrimitive.Content
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"mt-2 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
TabsContent.displayName = TabsPrimitive.Content.displayName
|
||||
|
||||
export { Tabs, TabsList, TabsTrigger, TabsContent }
|
Loading…
Reference in New Issue
Block a user