diff --git a/apps/site/public/illustrations/client/client_0.svg b/apps/site/public/illustrations/client/client_0.svg new file mode 100644 index 0000000000..1f56717e05 --- /dev/null +++ b/apps/site/public/illustrations/client/client_0.svg @@ -0,0 +1,101 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/site/public/illustrations/client/client_0_light.svg b/apps/site/public/illustrations/client/client_0_light.svg new file mode 100644 index 0000000000..51444c8f8a --- /dev/null +++ b/apps/site/public/illustrations/client/client_0_light.svg @@ -0,0 +1,101 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/site/public/illustrations/client/client_1.svg b/apps/site/public/illustrations/client/client_1.svg new file mode 100644 index 0000000000..998fb14935 --- /dev/null +++ b/apps/site/public/illustrations/client/client_1.svg @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/site/public/illustrations/client/client_1_light.svg b/apps/site/public/illustrations/client/client_1_light.svg new file mode 100644 index 0000000000..b48691cb92 --- /dev/null +++ b/apps/site/public/illustrations/client/client_1_light.svg @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/site/src/app/client/page.tsx b/apps/site/src/app/client/page.tsx new file mode 100644 index 0000000000..48210db38e --- /dev/null +++ b/apps/site/src/app/client/page.tsx @@ -0,0 +1,328 @@ +import type { Metadata } from "next"; +import { + SITE_HOME_DESCRIPTION, + SITE_HOME_TITLE, +} from "../../lib/blog-metadata"; +import { Action, Button, Card } from "@prisma/eclipse"; +import API from "@/components/client/api"; +import { CardSection } from "@/components/homepage/card-section/card-section"; +import { cn } from "@/lib/cn"; +import { Technology } from "@/components/client/technology"; + +export const metadata: Metadata = { + title: SITE_HOME_TITLE, + description: SITE_HOME_DESCRIPTION, +}; + +const databases = { + title: "Supported Databases", + list: [ + { + name: "PostgreSQL", + icon: "/icons/companies/postgres.svg", + url: "/", + }, + { + name: "MySQL", + icon: "/icons/technologies/mysqlsimple.svg", + url: "/", + }, + { + name: "MariaDB", + icon: "/icons/technologies/mariadb.svg", + url: "/", + }, + { + name: "SQLite", + icon: "/icons/companies/sqlite.svg", + url: "/", + }, + { + name: "SQL Server", + icon: "/icons/companies/sqlserver.svg", + url: "/", + }, + { + name: "CockroachDB", + icon: "/icons/companies/cockroachdb.svg", + url: "/", + }, + { + name: "PlanetScale", + icon: "/icons/companies/planetscale.svg", + url: "/", + }, + { + name: "MongoDB", + icon: "/icons/technologies/mongodbsimple.svg", + url: "/", + }, + ], +}; +const frameworks = { + title: "Selected Frameworks", + description: + "Easy to integrate into your framework of choice, Prisma simplifies database access, saves repetitive CRUD boilerplate and increases type safety.", + list: [ + { + name: "React", + icon: "/icons/technologies/react.svg", + url: "/react", + }, + { + name: "Next.js", + icon: "/icons/technologies/nextjs.svg", + url: "/nextjs", + }, + { + name: "NestJS", + icon: "/icons/technologies/nestjs.svg", + url: "/nestjs", + }, + { + name: "Apollo", + icon: "/icons/technologies/apollo.svg", + url: "/apollo", + }, + { + name: "Hapi", + icon: "/icons/technologies/hapi.svg", + url: "/hapi", + }, + { + name: "GraphQL", + icon: "/icons/technologies/graphql.svg", + url: "/graphql", + }, + { + name: "ExpressJS", + icon: "/icons/technologies/express.svg", + url: "/express", + }, + { + name: "Redwood", + icon: "/icons/technologies/redwoodjs.svg", + url: "/redwood", + }, + ], +}; +const twoCol = [ + { + content: ( +
+
+
+ Editor Integration +
+

+ Autocomplete your way to success +

+
+

+ The best code is the code that writes itself. Prisma Client gives you + a fantastic autocomplete experience so you can move quickly and be + sure you don't write an invalid query. Our obsession with type safety + means you can rest assured that your code works as expected, every + time. +

+ +
+ ), + imageUrl: "/illustrations/client/client_0", + imageAlt: "Autocomplete your way to success", + mobileImageUrl: null, + mobileImageAlt: null, + logos: null, + useDefaultLogos: false, + visualPosition: "left" as const, + visualType: "image" as const, + }, + { + content: ( +
+
+
+ TypedSQL +
+

+ Fully type-safe raw SQL +

+
+

+ Execute SQL queries directly against your database without losing the + benefits of Prisma’s type-checking and auto-completion. TypedSQL + leverages the capabilities of Prisma Client to write raw SQL queries + that are type-checked at compile time. +

+ +
+ ), + imageUrl: "/illustrations/client/client_1", + imageAlt: "Fully type-safe raw SQL", + mobileImageUrl: null, + mobileImageAlt: null, + logos: null, + useDefaultLogos: false, + noShadow: true, + visualPosition: "right" as const, + visualType: "image" as const, + }, +]; + +export default function Client() { + return ( +
+
+
+
+
+ Prisma Client +
+

+ Intuitive database client for TypeScript and Node.js +
+ Database Migrations +

+
+

+ The Prisma Client works seamlessly across languages and databases. + Ship faster by writing less SQL. Avoid mistakes with a fully type-safe + API tailored specifically for your app. +

+
+
+
+ +
+
+
+
+ +
+
+
+
+
+

+ Works with your favourite +
+ databases and framework +

+ +
+ {databases.title} +
+
+ {databases.list.map((db) => ( + + + {db.name} + + + ))} +
+
+ +
+
+ {frameworks.title} +
+

+ {frameworks.description} +

+
+
+ {frameworks.list.map((fw) => ( + + + {fw.name} + + + ))} +
+
+
+ + +
+
+
+
+
+ Prisma Studio +
+

+ Visual database browser +

+

+ Prisma Studio is the easiest way to explore and manipulate data + in your Prisma projects. Understand your data by browsing across + tables, filter, paginate, traverse relations and edit your data + with safety. +

+ +
+
+
+ Prisma Migrate +
+

+ Hassle-free migrations +

+

+ Prisma Migrate auto-generates SQL migrations from your Prisma + schema. These migration files are fully customizable, giving you + full control and ultimate flexibility — from local development + to production environments. +

+ +
+
+
+
+
+ ); +} diff --git a/apps/site/src/app/pricing/pricing-hero-plans.tsx b/apps/site/src/app/pricing/pricing-hero-plans.tsx index a882dfb451..363bafbf8a 100644 --- a/apps/site/src/app/pricing/pricing-hero-plans.tsx +++ b/apps/site/src/app/pricing/pricing-hero-plans.tsx @@ -70,7 +70,7 @@ export function PricingHeroPlans({ - + {Object.entries(symbols).map(([code, symbol]) => ( {code} {symbol} @@ -88,7 +88,9 @@ export function PricingHeroPlans({
{highlighted && ( @@ -129,7 +131,8 @@ export function PricingHeroPlans({

{plan.price[currency]} - { ' ' } / month + {" "} + / month

+ +
+ +
+ {} + +
+ {contentBlocks} +
+ + ); +}; + +export default API; diff --git a/apps/site/src/components/client/technology.tsx b/apps/site/src/components/client/technology.tsx new file mode 100644 index 0000000000..ab24464528 --- /dev/null +++ b/apps/site/src/components/client/technology.tsx @@ -0,0 +1,36 @@ +"use client"; +import { + Button, + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from "@prisma/eclipse"; +import { useState } from "react"; + +export const Technology = ({ + children, + text, + url, +}: { + children: React.ReactNode; + text: string; + url?: string; +}) => { + return ( + + + + + + {text} + + + ); +}; diff --git a/apps/site/src/components/navigation-wrapper.tsx b/apps/site/src/components/navigation-wrapper.tsx index 2f62a01b78..b53fe12338 100644 --- a/apps/site/src/components/navigation-wrapper.tsx +++ b/apps/site/src/components/navigation-wrapper.tsx @@ -35,6 +35,7 @@ const orm = [ "/newsletter", "/typedsql", "/partners", + "/client", ]; type ColorType = "orm" | "ppg" | undefined; diff --git a/packages/ui/src/components/footer.tsx b/packages/ui/src/components/footer.tsx index 465f008a64..d293cea09e 100644 --- a/packages/ui/src/components/footer.tsx +++ b/packages/ui/src/components/footer.tsx @@ -22,9 +22,16 @@ type LinkProps = AnchorHTMLAttributes & { }; const Link = ({ external, color, children, href, ...rest }: LinkProps) => { + const hoverClass = + color === "orm" + ? "hover:bg-background-orm-strong" + : color === "ppg" + ? "hover:bg-background-ppg-strong" + : ""; + const className = cn( "text-foreground-neutral-weak text-md font-semibold leading-md flex items-center cursor-pointer font-medium box-border no-underline px-2.5 -ml-2.5 py-1.5 transition-colors rounded-square transition-all", - color && `hover:bg-background-${color}-strong`, + hoverClass, ); if (external || !href || href.startsWith("http") || href.startsWith("#")) { @@ -85,25 +92,34 @@ const Footer = ({
- {footerData.socialIcons.map((socialLink: any, idx: number) => ( - div]:bg-background-${color}-strong`, - )} - > - - - - - ))} + {footerData.socialIcons.map((socialLink: any, idx: number) => { + const socialHoverClass = + color === "orm" + ? "hover:[&>div]:bg-background-orm-strong" + : color === "ppg" + ? "hover:[&>div]:bg-background-ppg-strong" + : ""; + + return ( + + + + + + ); + })}
{/* Main Grid Row */} @@ -133,7 +149,11 @@ const Footer = ({ @@ -143,20 +163,27 @@ const Footer = ({ {link.links.map( - (dropLink: { title: string; url: string }) => ( - - { + const dropdownHoverClass = + color === "orm" + ? "hover:bg-background-orm-strong!" + : "hover:bg-background-ppg-strong!"; + + return ( + - {dropLink.title} - - - ), + + {dropLink.title} + + + ); + }, )} diff --git a/packages/ui/src/components/navigation-menu.tsx b/packages/ui/src/components/navigation-menu.tsx index 8d2794a5c4..81d1dcbad4 100644 --- a/packages/ui/src/components/navigation-menu.tsx +++ b/packages/ui/src/components/navigation-menu.tsx @@ -109,18 +109,36 @@ function NavigationMenuItem({ } const navigationMenuTriggerStyle = cva( - "bg-transparent hover:bg-background-ppg-strong data-open:hover:bg-background-ppg-strong data-open:focus:bg-background-ppg-strong data-open:bg-background-ppg-strong focus-visible:ring-ring/50 data-popup-open:bg-background-ppg-strong data-popup-open:hover:bg-background-ppg-strong rounded-none! px-2.5 py-1.5 text-base font-semibold transition-all focus-visible:ring-1 focus-visible:outline-1 disabled:opacity-50 group/navigation-menu-trigger inline-flex h-9 w-max items-center justify-center disabled:pointer-events-none outline-none md:rounded-square! md:overflow-hidden cursor-pointer", + "bg-transparent rounded-none! px-2.5 py-1.5 text-base font-semibold transition-all focus-visible:ring-1 focus-visible:outline-1 disabled:opacity-50 group/navigation-menu-trigger inline-flex h-9 w-max items-center justify-center disabled:pointer-events-none outline-none md:rounded-square! md:overflow-hidden cursor-pointer focus-visible:ring-ring/50", + { + variants: { + variant: { + ppg: "hover:bg-background-ppg-strong data-open:hover:bg-background-ppg-strong data-open:focus:bg-background-ppg-strong data-open:bg-background-ppg-strong data-popup-open:bg-background-ppg-strong data-popup-open:hover:bg-background-ppg-strong", + orm: "hover:bg-background-orm-strong data-open:hover:bg-background-orm-strong data-open:focus:bg-background-orm-strong data-open:bg-background-orm-strong data-popup-open:bg-background-orm-strong data-popup-open:hover:bg-background-orm-strong", + }, + }, + defaultVariants: { + variant: "ppg", + }, + }, ); function NavigationMenuTrigger({ className, children, + variant = "ppg", ...props -}: NavigationMenuPrimitive.Trigger.Props) { +}: NavigationMenuPrimitive.Trigger.Props & { + variant?: "ppg" | "orm"; +}) { return ( {children}{" "} @@ -179,13 +197,21 @@ function NavigationMenuPositioner({ function NavigationMenuLink({ className, + variant = "ppg", ...props -}: NavigationMenuPrimitive.Link.Props) { +}: React.ComponentProps & { + variant?: "ppg" | "orm"; +}) { + const variantClasses = + variant === "orm" + ? "data-active:focus:bg-background-orm-strong data-active:hover:bg-background-orm-strong data-active:bg-background-orm-strong hover:bg-background-orm-strong" + : "data-active:focus:bg-background-ppg-strong data-active:hover:bg-background-ppg-strong data-active:bg-background-ppg-strong hover:bg-background-ppg-strong"; + return ( [number]; + variant?: "ppg" | "orm"; }) { + const hoverClass = + variant === "orm" + ? "hover:bg-background-orm-strong" + : "hover:bg-background-ppg-strong"; + const iconColor = + variant === "orm" + ? "text-background-orm-reverse" + : "text-background-ppg-reverse"; + return ( {link.icon ? ( - - + + ) : null}
@@ -326,15 +367,32 @@ function MenuNavigationItem({ } // Add this new component before NavigationMobileMenu -function MobileMenuItemWithSubmenu({ link }: { link: WebNavigationLink }) { +function MobileMenuItemWithSubmenu({ + link, + variant = "ppg", +}: { + link: WebNavigationLink; + variant?: "ppg" | "orm"; +}) { const [isOpen, setOpen] = useState(false); + const hoverClass = + variant === "orm" + ? "hover:bg-background-orm-strong! data-open:hover:bg-background-orm-strong! data-open:bg-background-orm-strong! data-popup-open:bg-background-orm-strong! data-popup-open:hover:bg-background-orm-strong!" + : "hover:bg-background-ppg-strong! data-open:hover:bg-background-ppg-strong! data-open:bg-background-ppg-strong! data-popup-open:bg-background-ppg-strong! data-popup-open:hover:bg-background-ppg-strong!"; + const openClass = + variant === "orm" + ? "bg-background-orm-strong!" + : "bg-background-ppg-strong!"; + return ( setOpen(!isOpen)} > @@ -343,7 +401,11 @@ function MobileMenuItemWithSubmenu({ link }: { link: WebNavigationLink }) { {isOpen && link.sub && ( {link.sub.map((sublink) => ( - + ))} )} @@ -376,7 +438,11 @@ function NavigationMobileMenu({ ) : link.sub?.length ? ( - + ) : null, )}
diff --git a/packages/ui/src/components/web-navigation.tsx b/packages/ui/src/components/web-navigation.tsx index d746665916..7b5dbe14de 100644 --- a/packages/ui/src/components/web-navigation.tsx +++ b/packages/ui/src/components/web-navigation.tsx @@ -81,13 +81,15 @@ export function WebNavigation({ {links.map((link) => link.url ? ( - + {link.text} ) : link?.sub?.length ? ( - {link.text} + + {link.text} +
))}