sanity-studio integrated

This commit is contained in:
Cypro Freelance 2024-08-08 14:51:56 +05:30
parent 61dafc3f3f
commit 497b7d091e
20 changed files with 25057 additions and 89 deletions

View file

@ -10,3 +10,5 @@ NEXTAUTH_SECRET=
EMAIL= EMAIL=
EMAIL_PASS= EMAIL_PASS=
SANITY_PROJECT_ID=

View file

@ -0,0 +1,67 @@
import { client, urlFor } from "@/lib/sanity";
import { PortableText } from "@portabletext/react";
import Image from "next/image";
import Link from "next/link";
import { ArrowLeft } from "lucide-react";
import { Separator } from "@/components/ui/separator";
async function getData(slug: string) {
const query = `
*[_type == "blog" && slug.current == '${slug}'] {
"currentSlug": slug.current,
title,
content,
titleImage
}[0]`;
const data = await client.fetch(query);
return data;
}
interface BlogSlugArticle {
currentSlug: string;
title: string;
content: any;
titleImage: string;
}
export default async function BlogSlugArticle({
params,
}: {
params: { slug: string };
}) {
const data: BlogSlugArticle = await getData(params.slug);
return (
<section className="max-w-5xl container mx-auto py-24 md:py-28 flex flex-col items-center px-4">
<Link
href="/blog"
className="self-start mb-8 text-primary hover:text-green-300 transition-all flex items-center"
>
<ArrowLeft className="mr-2" />
Back to Blog
</Link>
<header className="text-center mb-12 w-full">
{data.titleImage && (
<div className="mb-8">
<h1 className="text-3xl md:text-4xl mb-12 font-bold text-black dark:bg-clip-text dark:text-transparent dark:bg-gradient-to-b dark:from-white dark:to-neutral-400">
{data.title}
</h1>
<Image
src={urlFor(data.titleImage).url()}
alt={data.title}
width={1200}
height={800}
priority
className="w-full h-auto object-cover rounded-md"
/>
</div>
)}
</header>
<Separator className="mb-6" />
<article className="prose max-w-full md:prose-lg dark:prose-invert">
<PortableText value={data.content} />
</article>
</section>
);
}

View file

@ -5,11 +5,16 @@ import BlogCards from "@/components/cards/BlogCards";
export const metadata: Metadata = { export const metadata: Metadata = {
title: "Blog - SVRJS", title: "Blog - SVRJS",
}; };
const BlogPage = () => { const BlogPage = () => {
return ( return (
<section id="blog" className="mb-12 grid gap-6 p-6 md:gap-12"> <section
<h2 className="text-3xl sm:text-4xl">SVRJS BlogPage</h2> id="blog"
className="wrapper container py-24 md:py-28 gap-2 flex-center flex-col"
>
<h1 className="text-3xl md:text-5xl mb-12 pb-1 md:pb-2 font-bold text-black dark:bg-clip-text dark:text-transparent dark:bg-gradient-to-b dark:from-white dark:to-neutral-400">
SVRJS Blog Post
</h1>
<BlogCards /> <BlogCards />
</section> </section>
); );

View file

@ -2,37 +2,66 @@ import React from "react";
import Link from "next/link"; import Link from "next/link";
import Image from "next/image"; import Image from "next/image";
import { ExternalLink } from "lucide-react"; import { ExternalLink } from "lucide-react";
import { client, urlFor } from "@/lib/sanity";
import { Card, CardContent } from "../ui/card";
interface BlogPostcard {
title: string;
smallDescription: string;
currentSlug: string;
titleImage: string;
}
async function getData() {
const query = `*[_type == 'blog'] | order(_createdAt desc) {
title,
smallDescription,
"currentSlug": slug.current,
titleImage
}`;
const data = await client.fetch(query);
return data;
}
const BlogCards = async () => {
const data: BlogPostcard[] = await getData();
console.log(data);
const BlogCards = () => {
return ( return (
<section className="grid max-w-6xl gap-2 fade-in sm:grid-cols-2 lg:grid-cols-3"> <section className="grid max-w-6xl gap-4 mx-auto sm:grid-cols-2 lg:grid-cols-3">
<div className="fade-in-bottom group h-fit w-fit rounded-lg border delay-300"> {data.map((post, idx) => (
<Link <Card
href="/blog" className="group h-full w-full rounded-lg border overflow-hidden"
className="relative block overflow-hidden rounded-lg border" key={idx}
> >
<div className="h-full w-full overflow-hidden"> <Link href={`/blog/${post.currentSlug}`} className="block">
<Image <div className="relative overflow-hidden rounded-t-lg">
src={"/metadata/svrjs-cover.png"} <Image
alt={"svrjs-cover"} src={urlFor(post.titleImage).url()}
width={500} alt="SVRJS Blog Cover"
height={50} width={500}
className="h-full w-full object-cover object-center transition-all md:group-hover:scale-[1.01]" height={300}
/> className="w-full object-cover transition-transform duration-200 group-hover:scale-105"
</div> />
<div className="flex w-full flex-col justify-between gap-2 rounded-b-lg border-t bg-accent/25 p-4 md:flex-row md:items-start md:p-2 md:group-hover:bg-accent/50">
<div>
<p>Svrjs Node Server</p>
<span className="text-sm text-muted-foreground">
Description here
</span>
</div> </div>
<p className="text-sm text-muted-foreground opacity-0 group-hover:opacity-100 duration-300"> <CardContent className="p-4">
<ExternalLink /> <div className="flex-between mb-2 py-2 ">
</p> <h3 className="text-xl font-semibold leading-tight">
</div> {post.title}
</Link> </h3>
</div> <div className="text-sm text-muted-foreground opacity-0 group-hover:opacity-100 duration-300">
<ExternalLink />
</div>
</div>
<p className="text-sm text-muted-foreground">
{post.smallDescription}
</p>
</CardContent>
</Link>
</Card>
))}
</section> </section>
); );
}; };

View file

@ -1,6 +1,6 @@
import * as React from "react"; import * as React from "react"
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils"
const Card = React.forwardRef< const Card = React.forwardRef<
HTMLDivElement, HTMLDivElement,
@ -10,12 +10,12 @@ const Card = React.forwardRef<
ref={ref} ref={ref}
className={cn( className={cn(
"rounded-lg border bg-card text-card-foreground shadow-sm", "rounded-lg border bg-card text-card-foreground shadow-sm",
className, className
)} )}
{...props} {...props}
/> />
)); ))
Card.displayName = "Card"; Card.displayName = "Card"
const CardHeader = React.forwardRef< const CardHeader = React.forwardRef<
HTMLDivElement, HTMLDivElement,
@ -26,8 +26,8 @@ const CardHeader = React.forwardRef<
className={cn("flex flex-col space-y-1.5 p-6", className)} className={cn("flex flex-col space-y-1.5 p-6", className)}
{...props} {...props}
/> />
)); ))
CardHeader.displayName = "CardHeader"; CardHeader.displayName = "CardHeader"
const CardTitle = React.forwardRef< const CardTitle = React.forwardRef<
HTMLParagraphElement, HTMLParagraphElement,
@ -37,12 +37,12 @@ const CardTitle = React.forwardRef<
ref={ref} ref={ref}
className={cn( className={cn(
"text-2xl font-semibold leading-none tracking-tight", "text-2xl font-semibold leading-none tracking-tight",
className, className
)} )}
{...props} {...props}
/> />
)); ))
CardTitle.displayName = "CardTitle"; CardTitle.displayName = "CardTitle"
const CardDescription = React.forwardRef< const CardDescription = React.forwardRef<
HTMLParagraphElement, HTMLParagraphElement,
@ -53,16 +53,16 @@ const CardDescription = React.forwardRef<
className={cn("text-sm text-muted-foreground", className)} className={cn("text-sm text-muted-foreground", className)}
{...props} {...props}
/> />
)); ))
CardDescription.displayName = "CardDescription"; CardDescription.displayName = "CardDescription"
const CardContent = React.forwardRef< const CardContent = React.forwardRef<
HTMLDivElement, HTMLDivElement,
React.HTMLAttributes<HTMLDivElement> React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<div ref={ref} className={cn("p-6 pt-0", className)} {...props} /> <div ref={ref} className={cn("p-6 pt-0", className)} {...props} />
)); ))
CardContent.displayName = "CardContent"; CardContent.displayName = "CardContent"
const CardFooter = React.forwardRef< const CardFooter = React.forwardRef<
HTMLDivElement, HTMLDivElement,
@ -73,14 +73,7 @@ const CardFooter = React.forwardRef<
className={cn("flex items-center p-6 pt-0", className)} className={cn("flex items-center p-6 pt-0", className)}
{...props} {...props}
/> />
)); ))
CardFooter.displayName = "CardFooter"; CardFooter.displayName = "CardFooter"
export { export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent }
Card,
CardHeader,
CardFooter,
CardTitle,
CardDescription,
CardContent,
};

15
lib/sanity.ts Normal file
View file

@ -0,0 +1,15 @@
import { createClient } from "next-sanity";
import imageUrlBuilder from "@sanity/image-url";
export const client = createClient({
apiVersion: "2023-05-03",
dataset: "production",
projectId: `${process.env.SANITY_PROJECT_ID}`,
useCdn: false, // basically enable this for faster loading time
});
const builder = imageUrlBuilder(client);
export function urlFor(source: any) {
return builder.image(source);
}

View file

@ -1,7 +1,17 @@
import nextra from "nextra"; import nextra from "nextra";
/** @type {import('next').NextConfig} */ /** @type {import('next').NextConfig} */
const NextConfig = {}; const NextConfig = {
images: {
remotePatterns: [
{
protocol: "https",
hostname: "cdn.sanity.io",
port: "",
},
],
},
};
const withNextra = nextra({ const withNextra = nextra({
theme: "nextra-theme-docs", theme: "nextra-theme-docs",

10386
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -12,6 +12,7 @@
"dependencies": { "dependencies": {
"@hookform/resolvers": "^3.6.0", "@hookform/resolvers": "^3.6.0",
"@mdx-js/mdx": "^3.0.1", "@mdx-js/mdx": "^3.0.1",
"@portabletext/react": "^3.1.0",
"@radix-ui/react-accordion": "^1.1.2", "@radix-ui/react-accordion": "^1.1.2",
"@radix-ui/react-dialog": "^1.1.1", "@radix-ui/react-dialog": "^1.1.1",
"@radix-ui/react-dropdown-menu": "^2.1.1", "@radix-ui/react-dropdown-menu": "^2.1.1",
@ -22,6 +23,7 @@
"@radix-ui/react-slot": "^1.1.0", "@radix-ui/react-slot": "^1.1.0",
"@radix-ui/react-toast": "^1.2.1", "@radix-ui/react-toast": "^1.2.1",
"@radix-ui/themes": "^3.0.5", "@radix-ui/themes": "^3.0.5",
"@sanity/image-url": "^1.0.2",
"@tailwindcss/typography": "^0.5.13", "@tailwindcss/typography": "^0.5.13",
"@types/bcrypt": "^5.0.2", "@types/bcrypt": "^5.0.2",
"@types/cookie": "^0.6.0", "@types/cookie": "^0.6.0",
@ -40,6 +42,7 @@
"mongoose": "^8.4.3", "mongoose": "^8.4.3",
"next-auth": "^4.24.7", "next-auth": "^4.24.7",
"next-mdx-remote": "^5.0.0", "next-mdx-remote": "^5.0.0",
"next-sanity": "^9.4.4",
"next-themes": "^0.3.0", "next-themes": "^0.3.0",
"nextra": "^2.13.4", "nextra": "^2.13.4",
"nextra-theme-docs": "^2.13.4", "nextra-theme-docs": "^2.13.4",

3
svrjs/.eslintrc Normal file
View file

@ -0,0 +1,3 @@
{
"extends": "@sanity/eslint-config-studio"
}

29
svrjs/.gitignore vendored Normal file
View file

@ -0,0 +1,29 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# Dependencies
/node_modules
/.pnp
.pnp.js
# Compiled Sanity Studio
/dist
# Temporary Sanity runtime, generated by the CLI on every dev server start
/.sanity
# Logs
/logs
*.log
# Coverage directory used by testing tools
/coverage
# Misc
.DS_Store
*.pem
# Typescript
*.tsbuildinfo
# Dotenv and similar local-only files
*.local

9
svrjs/README.md Normal file
View file

@ -0,0 +1,9 @@
# Sanity Clean Content Studio
Congratulations, you have now installed the Sanity Content Studio, an open-source real-time content editing environment connected to the Sanity backend.
Now you can do the following things:
- [Read “getting started” in the docs](https://www.sanity.io/docs/introduction/getting-started?utm_source=readme)
- [Join the community Slack](https://slack.sanity.io/?utm_source=readme)
- [Extend and build plugins](https://www.sanity.io/docs/content-studio/extending?utm_source=readme)

14359
svrjs/package-lock.json generated Normal file

File diff suppressed because it is too large Load diff

37
svrjs/package.json Normal file
View file

@ -0,0 +1,37 @@
{
"name": "svrjs",
"private": true,
"version": "1.0.0",
"main": "package.json",
"license": "UNLICENSED",
"scripts": {
"dev": "sanity dev",
"start": "sanity start",
"build": "sanity build",
"deploy": "sanity deploy",
"deploy-graphql": "sanity graphql deploy"
},
"keywords": [
"sanity"
],
"dependencies": {
"@sanity/vision": "^3.53.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"sanity": "^3.53.0",
"styled-components": "^6.1.8"
},
"devDependencies": {
"@sanity/eslint-config-studio": "^4.0.0",
"@types/react": "^18.0.25",
"eslint": "^8.6.0",
"prettier": "^3.0.2",
"typescript": "^5.1.6"
},
"prettier": {
"semi": false,
"printWidth": 100,
"bracketSpacing": false,
"singleQuote": true
}
}

8
svrjs/sanity.cli.ts Normal file
View file

@ -0,0 +1,8 @@
import {defineCliConfig} from 'sanity/cli'
export default defineCliConfig({
api: {
projectId: '0u8q501s',
dataset: 'production'
}
})

18
svrjs/sanity.config.ts Normal file
View file

@ -0,0 +1,18 @@
import {defineConfig} from 'sanity'
import {structureTool} from 'sanity/structure'
import {visionTool} from '@sanity/vision'
import {schemaTypes} from './schemaTypes'
export default defineConfig({
name: 'default',
title: 'svrjs',
projectId: '0u8q501s',
dataset: 'production',
plugins: [structureTool(), visionTool()],
schema: {
types: schemaTypes,
},
})

40
svrjs/schemaTypes/blog.ts Normal file
View file

@ -0,0 +1,40 @@
export default {
name: 'blog',
type: 'document',
title: 'Blog',
fields: [
{
name: 'title',
type: 'string',
title: 'Title',
},
{
name: 'slug',
type: 'slug',
title: 'Slug Title',
options: {
source: 'title',
},
},
{
name: 'titleImage',
type: 'image',
title: 'Title Image',
},
{
name: 'smallDescription',
type: 'text',
title: 'Small Description',
},
{
name: 'content',
type: 'array',
title: 'Content',
of: [
{
type: 'block',
},
],
},
],
}

View file

@ -0,0 +1,3 @@
import blog from './blog'
export const schemaTypes = [blog]

1
svrjs/static/.gitkeep Normal file
View file

@ -0,0 +1 @@
Files placed here will be served by the Sanity server under the `/static`-prefix

17
svrjs/tsconfig.json Normal file
View file

@ -0,0 +1,17 @@
{
"compilerOptions": {
"target": "ES2017",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"module": "Preserve",
"moduleDetection": "force",
"isolatedModules": true,
"jsx": "preserve",
"incremental": true
},
"include": ["**/*.ts", "**/*.tsx"],
"exclude": ["node_modules"]
}