diff options
| author | Dominik K. <dominik@koch-bautechnik.de> | 2025-07-30 15:20:44 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-07-30 16:20:44 +0300 |
| commit | 1c20027d33bcf303d6a9b40970cd8216c489f0d5 (patch) | |
| tree | 1c8aca5ace5011bc5125da955a58cfb90de61158 /apps | |
| parent | deda0e366fc934f9734d4c523e46fa8b51111237 (diff) | |
| download | opencut-1c20027d33bcf303d6a9b40970cd8216c489f0d5.tar.gz | |
feat: create external tools section (#493)
* feat: create external tools section
* fix: better wording
Diffstat (limited to 'apps')
| -rw-r--r-- | apps/web/package.json | 2 | ||||
| -rw-r--r-- | apps/web/src/app/contributors/page.tsx | 57 | ||||
| -rw-r--r-- | apps/web/src/components/icons.tsx | 50 | ||||
| -rw-r--r-- | apps/web/src/constants/site.ts | 22 |
4 files changed, 128 insertions, 3 deletions
diff --git a/apps/web/package.json b/apps/web/package.json index f5cd7da4..9531b6c7 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -43,7 +43,7 @@ "input-otp": "^1.4.1", "lucide-react": "^0.468.0", "motion": "^12.18.1", - "next": "^15.4.3", + "next": "^15.4.5", "next-themes": "^0.4.4", "pg": "^8.16.2", "radix-ui": "^1.4.2", diff --git a/apps/web/src/app/contributors/page.tsx b/apps/web/src/app/contributors/page.tsx index 6a524866..29151bd8 100644 --- a/apps/web/src/app/contributors/page.tsx +++ b/apps/web/src/app/contributors/page.tsx @@ -5,8 +5,14 @@ import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; import { Button } from "@/components/ui/button"; import { ExternalLink } from "lucide-react"; import Link from "next/link"; -import { GithubIcon } from "@/components/icons"; +import { + GithubIcon, + MarbleIcon, + VercelIcon, + DataBuddyIcon, +} from "@/components/icons"; import { Badge } from "@/components/ui/badge"; +import { EXTERNAL_TOOLS } from "@/constants/site"; export const metadata: Metadata = { title: "Contributors - OpenCut", @@ -39,7 +45,7 @@ async function getContributors(): Promise<Contributor[]> { "User-Agent": "OpenCut-Web-App", }, next: { revalidate: 600 }, // 10 minutes - } + } as RequestInit ); if (!response.ok) { @@ -236,6 +242,53 @@ export default async function ContributorsPage() { </div> )} + <div className="mt-6 mb-20"> + <div className="text-center mb-12"> + <h2 className="text-2xl font-semibold mb-2">External Tools</h2> + <p className="text-muted-foreground"> + Tools we use to build OpenCut + </p> + </div> + + <div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-6 max-w-4xl mx-auto"> + {EXTERNAL_TOOLS.map((tool, index) => { + const IconComponent = { + MarbleIcon, + VercelIcon, + DataBuddyIcon, + }[tool.icon]; + + return ( + <Link + key={tool.name} + href={tool.url} + target="_blank" + className="group block" + style={{ + animationDelay: `${index * 100}ms`, + }} + > + <Card className="h-full bg-background/80 backdrop-blur-sm border-2 group-hover:border-muted-foreground/20 transition-all duration-300 group-hover:shadow-xl"> + <CardContent className="p-6 text-center h-full flex flex-col"> + <div className="mb-4"> + <div className="w-12 h-12 mx-auto rounded-full bg-muted/50 flex items-center justify-center group-hover:bg-muted/70 transition-colors"> + <IconComponent className="h-6 w-6" size={24} /> + </div> + </div> + <h3 className="font-semibold text-lg mb-2 group-hover:text-foreground/80 transition-colors"> + {tool.name} + </h3> + <p className="text-sm text-muted-foreground flex-1"> + {tool.description} + </p> + </CardContent> + </Card> + </Link> + ); + })} + </div> + </div> + <div className="mt-32 text-center"> <div className="max-w-2xl mx-auto"> <h2 className="text-3xl font-bold mb-4">Join the community</h2> diff --git a/apps/web/src/components/icons.tsx b/apps/web/src/components/icons.tsx index 6099c052..43f953e8 100644 --- a/apps/web/src/components/icons.tsx +++ b/apps/web/src/components/icons.tsx @@ -113,3 +113,53 @@ export function BackgroundIcon({ className }: { className?: string }) { </svg> ); } + +export function MarbleIcon({ + className = "", + size = 32, +}: { + className?: string; + size?: number; +}) { + return ( + <svg + xmlns="http://www.w3.org/2000/svg" + width={size} + height={size} + className={className} + fill="none" + viewBox="0 0 256 256" + > + <path fill="#202027" d="M0 0h256v256H0z" /> + <path + fill="#fff" + d="M116.032 94.016q1.408.256 6.272 9.856 4.856 9.472 11.904 24.576l4.096 8.576a42.4 42.4 0 0 0 4.992-6.784q2.304-3.96 5.76-10.624 3.96-7.56 5.504-9.728 6.144-9.344 13.312-19.84.896-1.408 3.2-1.92a4.8 4.8 0 0 1 1.28-.128q3.072 0 6.784 2.56 3.84 2.56 4.992 5.248a4.8 4.8 0 0 1 .256 1.92q0 1.28-.128 1.92l-.64 2.944q-.256 1.664-.512 3.968a3200 3200 0 0 0-5.888 46.848 56 56 0 0 0-.512 8.064l-.256 4.096a32 32 0 0 0-.128 2.944q0 1.672-.384 2.304-.384.64-1.408.64-.768 0-2.304-.384-4.096-.896-6.272-3.2-2.176-2.432-2.432-6.656-.128-2.176-.128-6.784 0-4.736.256-14.464l.128-7.04.256-10.112q-1.152 1.024-5.888 8.064a328 328 0 0 0-9.088 14.464q-4.48 7.56-5.888 11.648-1.28 3.2-2.56 4.736-1.152 1.536-2.816 1.536-1.536 0-3.968-1.408-6.912-4.224-8.448-11.648a336 336 0 0 0-6.016-23.68 40 40 0 0 0-2.56-6.272q-1.792-3.968-2.304-4.992l-1.664-3.328q-18.68 24.832-25.344 45.568-1.28 4.352-2.432 6.272-1.152 1.792-2.688 1.792-2.432 0-6.912-4.352Q72 158.144 72 154.304q0-2.04.768-4.096a108 108 0 0 1 16-30.08l3.968-5.248a432 432 0 0 0 12.16-16.64q3.072-4.48 8.192-4.48.896 0 2.944.256" + /> + </svg> + ); +} + +export function DataBuddyIcon({ + className = "", + size = 32, +}: { + className?: string; + size?: number; +}) { + return ( + <svg + xmlns="http://www.w3.org/2000/svg" + width={size} + height={size} + className={className} + viewBox="0 0 8 8" + shapeRendering="crispEdges" + > + <path d="M0 0h8v8H0z" /> + <path + fill="#fff" + d="M1 1h1v6H1zm1 0h4v1H2zm4 1h1v1H6zm0 1h1v1H6zm0 1h1v1H6zm0 1h1v1H6zM2 6h4v1H2zm1-3h1v1H3zm1 1h1v1H4z" + /> + </svg> + ); +} diff --git a/apps/web/src/constants/site.ts b/apps/web/src/constants/site.ts index f47f6470..00bd649f 100644 --- a/apps/web/src/constants/site.ts +++ b/apps/web/src/constants/site.ts @@ -9,3 +9,25 @@ export const SITE_INFO = { twitterImage: "/open-graph/default.jpg", favicon: "/favicon.ico", }; + +export const EXTERNAL_TOOLS = [ + { + name: "Marble", + description: + "Modern headless CMS for content management and the blog for OpenCut", + url: "https://marblecms.com?utm_source=opencut", + icon: "MarbleIcon" as const, + }, + { + name: "Vercel", + description: "Platform where we deploy and host OpenCut", + url: "https://vercel.com?utm_source=opencut", + icon: "VercelIcon" as const, + }, + { + name: "Databuddy", + description: "GDPR compliant analytics and user insights for OpenCut", + url: "https://databuddy.cc?utm_source=opencut", + icon: "DataBuddyIcon" as const, + }, +]; |
