feat: add dashboard with dummy data

This commit is contained in:
2024-12-14 16:47:55 +01:00
parent 61bc2d10d7
commit 12036ff5e1
14 changed files with 1377 additions and 33 deletions

8
.prettierrc Normal file
View File

@@ -0,0 +1,8 @@
{
"useTabs": false,
"printWidth": 800,
"tabWidth": 2,
"singleQuote": true,
"trailingComma": "es5",
"semi": true
}

5
.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,5 @@
{
"editor.tabSize": 2,
"editor.detectIndentation": false,
"editor.insertSpaces": true,
}

View File

@@ -1,6 +1,7 @@
services:
psql:
image: postgres
user: 1000:1000
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: S+xyPXPDcYNQtTy3hUNoC9eBwmsoGA

View File

@@ -8,7 +8,8 @@
"start": "next start",
"lint": "next lint",
"db:generate": "prisma generate",
"db:migrate": "prisma migrate dev --name"
"db:migrate": "prisma migrate dev --name",
"ui:add": "shadcn add"
},
"dependencies": {
"@lucia-auth/adapter-prisma": "^4.0.1",
@@ -30,9 +31,10 @@
"sonner": "^1.4.41",
"tailwind-merge": "^2.2.2",
"tailwindcss-animate": "^1.0.7",
"zod": "^3.23.8"
"zod": "^3.24.1"
},
"devDependencies": {
"@faker-js/faker": "^9.3.0",
"@types/node": "^20",
"@types/react": "^18",
"@types/react-dom": "^18",
@@ -40,6 +42,7 @@
"eslint-config-next": "14.2.0",
"postcss": "^8",
"prisma": "^6.0.1",
"shadcn": "^2.1.7",
"tailwindcss": "^3.4.1",
"typescript": "^5"
},

View File

View File

@@ -0,0 +1,11 @@
import { ProjectCardSkeleton } from '@/components/app/ProjectCard/ProjectCard';
export default function Loading() {
return (
<div className="grid grid-cols-1 gap-8 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 p-4">
{Array.from({ length: 8 }).map((_, i) => (
<ProjectCardSkeleton key={i} />
))}
</div>
);
}

View File

@@ -0,0 +1,21 @@
import ProjectCard from '@/components/app/ProjectCard/ProjectCard';
import { faker } from '@faker-js/faker';
export const dummyData = Array.from({ length: 10 }, (_, id) => ({
id: id + 1,
name: faker.word.noun(),
description: faker.lorem.sentence(),
github: id !== 5 ? faker.internet.url() : undefined,
}));
export default function Page() {
return (
<>
<h1>Dashboard</h1>
<div className="grid grid-cols-1 gap-8 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 p-4">
{dummyData.map((d) => (
<ProjectCard key={d.id} {...d} />
))}
</div>
</>
);
}

View File

View File

@@ -1,12 +0,0 @@
import { validateRequest } from "@/lib/auth";
export default async function Page() {
const { user } = await validateRequest()
return (
<div>
<h1 className="text-3xl font-bold text-center">Welcome {user?.username}!</h1>
<p>You are actually on a protected route!</p>
<p>Your ID is: {user!.id}</p>
</div>
)
}

View File

@@ -18,8 +18,7 @@ import { ThemeSwitcher } from "../ThemeSwitcher/ThemeSwitcher"
export const links = [
{ href: '/', name: 'Home' },
{ href: 'https://github.com/SrIzan10/stack', name: 'Github' },
{ href: '/protected', name: 'Protected route' }
{ href: '/dashboard', name: 'Dashboard' }
]
function NavbarLinks() {

View File

@@ -0,0 +1,48 @@
import { dummyData } from '@/app/(protected)/dashboard/page';
import { Button } from '@/components/ui/button';
import { Card, CardContent, CardFooter, CardHeader, CardTitle } from '@/components/ui/card';
import { Skeleton } from '@/components/ui/skeleton';
import { Eye, Github } from 'lucide-react';
import Link from 'next/link';
export default function ProjectCard(props: (typeof dummyData)[0]) {
return (
<Card>
<CardHeader>
<CardTitle>{props.name}</CardTitle>
</CardHeader>
<CardContent>{props.description}</CardContent>
<CardFooter className="flex justify-end gap-5">
<Link href={`/project/${props.id}`}>
<Button size={'icon'}>
<Eye />
</Button>
</Link>
{props.github && (
<Link href={props.github}>
<Button size={'icon'}>
<Github />
</Button>
</Link>
)}
</CardFooter>
</Card>
);
}
export function ProjectCardSkeleton() {
return (
<Card>
<CardHeader>
<Skeleton className="h-6 w-[200px]" />
</CardHeader>
<CardContent>
<Skeleton className="h-4 w-full" />
</CardContent>
<CardFooter className="flex justify-end gap-5">
<Skeleton className="h-9 w-9 rounded-md" />
<Skeleton className="h-9 w-9 rounded-md" />
</CardFooter>
</Card>
);
}

View File

@@ -0,0 +1,79 @@
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-lg border bg-card text-card-foreground shadow-sm",
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(
"text-2xl 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 }

View File

@@ -0,0 +1,15 @@
import { cn } from "@/lib/utils"
function Skeleton({
className,
...props
}: React.HTMLAttributes<HTMLDivElement>) {
return (
<div
className={cn("animate-pulse rounded-md bg-muted", className)}
{...props}
/>
)
}
export { Skeleton }

1200
yarn.lock

File diff suppressed because it is too large Load Diff