├── app ├── knowledge │ ├── loading.tsx │ └── page.tsx ├── layout.tsx ├── templates │ └── page.tsx ├── globals.css ├── cases │ └── page.tsx ├── page.tsx └── about │ └── page.tsx ├── public ├── apple-icon.png ├── placeholder.jpg ├── icon-dark-32x32.png ├── icon-light-32x32.png ├── placeholder-logo.png ├── placeholder-user.jpg ├── icon.svg ├── placeholder-logo.svg └── placeholder.svg ├── postcss.config.mjs ├── lib └── utils.ts ├── next.config.mjs ├── components.json ├── tsconfig.json ├── components ├── ui │ ├── input.tsx │ ├── tabs.tsx │ ├── sheet.tsx │ ├── button.tsx │ ├── card.tsx │ └── dropdown-menu.tsx ├── theme-provider.tsx ├── site-footer.tsx ├── site-header.tsx └── i18n-provider.tsx ├── README.md ├── package.json ├── data ├── cases.ts └── knowledge.ts └── styles └── globals.css /app/knowledge/loading.tsx: -------------------------------------------------------------------------------- 1 | export default function Loading() { 2 | return null 3 | } 4 | -------------------------------------------------------------------------------- /public/apple-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/node/v0-dxcp/main/public/apple-icon.png -------------------------------------------------------------------------------- /public/placeholder.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/node/v0-dxcp/main/public/placeholder.jpg -------------------------------------------------------------------------------- /public/icon-dark-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/node/v0-dxcp/main/public/icon-dark-32x32.png -------------------------------------------------------------------------------- /public/icon-light-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/node/v0-dxcp/main/public/icon-light-32x32.png -------------------------------------------------------------------------------- /public/placeholder-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/node/v0-dxcp/main/public/placeholder-logo.png -------------------------------------------------------------------------------- /public/placeholder-user.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/node/v0-dxcp/main/public/placeholder-user.jpg -------------------------------------------------------------------------------- /postcss.config.mjs: -------------------------------------------------------------------------------- 1 | /** @type {import('postcss-load-config').Config} */ 2 | const config = { 3 | plugins: { 4 | '@tailwindcss/postcss': {}, 5 | }, 6 | } 7 | 8 | export default config 9 | -------------------------------------------------------------------------------- /lib/utils.ts: -------------------------------------------------------------------------------- 1 | import { clsx, type ClassValue } from 'clsx' 2 | import { twMerge } from 'tailwind-merge' 3 | 4 | export function cn(...inputs: ClassValue[]) { 5 | return twMerge(clsx(inputs)) 6 | } 7 | -------------------------------------------------------------------------------- /next.config.mjs: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = { 3 | typescript: { 4 | ignoreBuildErrors: true, 5 | }, 6 | images: { 7 | unoptimized: true, 8 | }, 9 | 10 | } 11 | 12 | export default nextConfig -------------------------------------------------------------------------------- /components.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://ui.shadcn.com/schema.json", 3 | "style": "new-york", 4 | "rsc": true, 5 | "tsx": true, 6 | "tailwind": { 7 | "config": "", 8 | "css": "app/globals.css", 9 | "baseColor": "neutral", 10 | "cssVariables": true, 11 | "prefix": "" 12 | }, 13 | "aliases": { 14 | "components": "@/components", 15 | "utils": "@/lib/utils", 16 | "ui": "@/components/ui", 17 | "lib": "@/lib", 18 | "hooks": "@/hooks" 19 | }, 20 | "iconLibrary": "lucide" 21 | } 22 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "lib": ["dom", "dom.iterable", "esnext"], 4 | "allowJs": true, 5 | "target": "ES6", 6 | "skipLibCheck": true, 7 | "strict": true, 8 | "noEmit": true, 9 | "esModuleInterop": true, 10 | "module": "esnext", 11 | "moduleResolution": "bundler", 12 | "resolveJsonModule": true, 13 | "isolatedModules": true, 14 | "jsx": "preserve", 15 | "incremental": true, 16 | "plugins": [ 17 | { 18 | "name": "next" 19 | } 20 | ], 21 | "paths": { 22 | "@/*": ["./*"] 23 | } 24 | }, 25 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], 26 | "exclude": ["node_modules"] 27 | } 28 | -------------------------------------------------------------------------------- /components/ui/input.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | 3 | import { cn } from '@/lib/utils' 4 | 5 | function Input({ className, type, ...props }: React.ComponentProps<'input'>) { 6 | return ( 7 | 18 | ) 19 | } 20 | 21 | export { Input } 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DXC community website 2 | 3 | *Automatically synced with your [v0.app](https://v0.app) deployments* 4 | 5 | [![Deployed on Vercel](https://img.shields.io/badge/Deployed%20on-Vercel-black?style=for-the-badge&logo=vercel)](https://vercel.com/pecommunity/v0-dxc-community-website) 6 | [![Built with v0](https://img.shields.io/badge/Built%20with-v0.app-black?style=for-the-badge)](https://v0.app/chat/r1sXdBAuSgK) 7 | 8 | ## Overview 9 | 10 | This repository will stay in sync with your deployed chats on [v0.app](https://v0.app). 11 | Any changes you make to your deployed app will be automatically pushed to this repository from [v0.app](https://v0.app). 12 | 13 | ## Deployment 14 | 15 | Your project is live at: 16 | 17 | **[https://vercel.com/pecommunity/v0-dxc-community-website](https://vercel.com/pecommunity/v0-dxc-community-website)** 18 | 19 | ## Build your app 20 | 21 | Continue building your app on: 22 | 23 | **[https://v0.app/chat/r1sXdBAuSgK](https://v0.app/chat/r1sXdBAuSgK)** 24 | 25 | ## How It Works 26 | 27 | 1. Create and modify your project using [v0.app](https://v0.app) 28 | 2. Deploy your chats from the v0 interface 29 | 3. Changes are automatically pushed to this repository 30 | 4. Vercel deploys the latest version from this repository -------------------------------------------------------------------------------- /public/icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | 14 | 15 | 17 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /components/theme-provider.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import * as React from "react" 4 | 5 | type Theme = "light" | "dark" 6 | 7 | type ThemeContextType = { 8 | theme: Theme 9 | setTheme: (theme: Theme) => void 10 | } 11 | 12 | const ThemeContext = React.createContext(undefined) 13 | 14 | export function ThemeProvider({ children }: { children: React.ReactNode }) { 15 | const [theme, setTheme] = React.useState("light") 16 | 17 | React.useEffect(() => { 18 | if (typeof window !== "undefined") { 19 | const stored = window.localStorage.getItem("theme") as Theme 20 | if (stored) { 21 | setTheme(stored) 22 | document.documentElement.classList.toggle("dark", stored === "dark") 23 | } 24 | } 25 | }, []) 26 | 27 | const handleSetTheme = React.useCallback((newTheme: Theme) => { 28 | setTheme(newTheme) 29 | if (typeof window !== "undefined") { 30 | window.localStorage.setItem("theme", newTheme) 31 | document.documentElement.classList.toggle("dark", newTheme === "dark") 32 | } 33 | }, []) 34 | 35 | return {children} 36 | } 37 | 38 | export function useTheme() { 39 | const context = React.useContext(ThemeContext) 40 | if (context === undefined) { 41 | return { 42 | theme: "light" as Theme, 43 | setTheme: () => {}, 44 | } 45 | } 46 | return context 47 | } 48 | -------------------------------------------------------------------------------- /app/layout.tsx: -------------------------------------------------------------------------------- 1 | import type React from "react" 2 | import type { Metadata } from "next" 3 | import { Geist, Geist_Mono } from "next/font/google" 4 | import { Analytics } from "@vercel/analytics/next" 5 | import "./globals.css" 6 | import { ThemeProvider } from "@/components/theme-provider" 7 | import { I18nProvider } from "@/components/i18n-provider" 8 | 9 | const _geist = Geist({ subsets: ["latin"] }) 10 | const _geistMono = Geist_Mono({ subsets: ["latin"] }) 11 | 12 | export const metadata: Metadata = { 13 | title: "DXCP - Developer Experience Center Program", 14 | description: "开发者体验中心计划 - 提升研发生产力与创新速度", 15 | generator: "v0.app", 16 | icons: { 17 | icon: [ 18 | { 19 | url: "/icon-light-32x32.png", 20 | media: "(prefers-color-scheme: light)", 21 | }, 22 | { 23 | url: "/icon-dark-32x32.png", 24 | media: "(prefers-color-scheme: dark)", 25 | }, 26 | { 27 | url: "/icon.svg", 28 | type: "image/svg+xml", 29 | }, 30 | ], 31 | apple: "/apple-icon.png", 32 | }, 33 | } 34 | 35 | export default function RootLayout({ 36 | children, 37 | }: Readonly<{ 38 | children: React.ReactNode 39 | }>) { 40 | return ( 41 | 42 | 43 | 44 | {children} 45 | 46 | 47 | 48 | 49 | ) 50 | } 51 | -------------------------------------------------------------------------------- /components/site-footer.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import Link from "next/link" 4 | import { Github } from "lucide-react" 5 | import { useI18n } from "@/components/i18n-provider" 6 | 7 | export function SiteFooter() { 8 | const { t } = useI18n() 9 | 10 | return ( 11 |
12 |
13 |
14 |
15 |

{t("footer.community")}

16 |
    17 |
  • 18 | 22 | 23 | {t("footer.github")} 24 | 25 |
  • 26 |
27 |
28 |
29 |

{t("footer.docs")}

30 |
    31 |
  • 32 | 33 | {t("nav.templates")} 34 | 35 |
  • 36 |
  • 37 | 38 | {t("nav.knowledge")} 39 | 40 |
  • 41 |
42 |
43 |
44 |
45 |

{t("footer.copyright")}

46 |
47 |
48 |
49 | ) 50 | } 51 | -------------------------------------------------------------------------------- /components/ui/tabs.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import * as React from "react" 4 | import * as TabsPrimitive from "@radix-ui/react-tabs" 5 | 6 | import { cn } from "@/lib/utils" 7 | 8 | const Tabs = TabsPrimitive.Root 9 | 10 | const TabsList = React.forwardRef< 11 | React.ElementRef, 12 | React.ComponentPropsWithoutRef 13 | >(({ className, ...props }, ref) => ( 14 | 22 | )) 23 | TabsList.displayName = TabsPrimitive.List.displayName 24 | 25 | const TabsTrigger = React.forwardRef< 26 | React.ElementRef, 27 | React.ComponentPropsWithoutRef 28 | >(({ className, ...props }, ref) => ( 29 | 37 | )) 38 | TabsTrigger.displayName = TabsPrimitive.Trigger.displayName 39 | 40 | const TabsContent = React.forwardRef< 41 | React.ElementRef, 42 | React.ComponentPropsWithoutRef 43 | >(({ className, ...props }, ref) => ( 44 | 52 | )) 53 | TabsContent.displayName = TabsPrimitive.Content.displayName 54 | 55 | export { Tabs, TabsList, TabsTrigger, TabsContent } 56 | -------------------------------------------------------------------------------- /components/ui/sheet.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import * as React from "react" 4 | import * as SheetPrimitive from "@radix-ui/react-dialog" 5 | import { X } from "lucide-react" 6 | 7 | import { cn } from "@/lib/utils" 8 | 9 | const Sheet = SheetPrimitive.Root 10 | 11 | const SheetTrigger = SheetPrimitive.Trigger 12 | 13 | const SheetClose = SheetPrimitive.Close 14 | 15 | const SheetPortal = SheetPrimitive.Portal 16 | 17 | const SheetOverlay = React.forwardRef< 18 | React.ElementRef, 19 | React.ComponentPropsWithoutRef 20 | >(({ className, ...props }, ref) => ( 21 | 29 | )) 30 | SheetOverlay.displayName = SheetPrimitive.Overlay.displayName 31 | 32 | const SheetContent = React.forwardRef< 33 | React.ElementRef, 34 | React.ComponentPropsWithoutRef 35 | >(({ className, children, ...props }, ref) => ( 36 | 37 | 38 | 46 | {children} 47 | 48 | 49 | Close 50 | 51 | 52 | 53 | )) 54 | SheetContent.displayName = SheetPrimitive.Content.displayName 55 | 56 | export { Sheet, SheetPortal, SheetOverlay, SheetTrigger, SheetContent, SheetClose } 57 | -------------------------------------------------------------------------------- /components/ui/button.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import { Slot } from '@radix-ui/react-slot' 3 | import { cva, type VariantProps } from 'class-variance-authority' 4 | 5 | import { cn } from '@/lib/utils' 6 | 7 | const buttonVariants = cva( 8 | "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive", 9 | { 10 | variants: { 11 | variant: { 12 | default: 'bg-primary text-primary-foreground hover:bg-primary/90', 13 | destructive: 14 | 'bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60', 15 | outline: 16 | 'border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50', 17 | secondary: 18 | 'bg-secondary text-secondary-foreground hover:bg-secondary/80', 19 | ghost: 20 | 'hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50', 21 | link: 'text-primary underline-offset-4 hover:underline', 22 | }, 23 | size: { 24 | default: 'h-9 px-4 py-2 has-[>svg]:px-3', 25 | sm: 'h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5', 26 | lg: 'h-10 rounded-md px-6 has-[>svg]:px-4', 27 | icon: 'size-9', 28 | 'icon-sm': 'size-8', 29 | 'icon-lg': 'size-10', 30 | }, 31 | }, 32 | defaultVariants: { 33 | variant: 'default', 34 | size: 'default', 35 | }, 36 | }, 37 | ) 38 | 39 | function Button({ 40 | className, 41 | variant, 42 | size, 43 | asChild = false, 44 | ...props 45 | }: React.ComponentProps<'button'> & 46 | VariantProps & { 47 | asChild?: boolean 48 | }) { 49 | const Comp = asChild ? Slot : 'button' 50 | 51 | return ( 52 | 57 | ) 58 | } 59 | 60 | export { Button, buttonVariants } 61 | -------------------------------------------------------------------------------- /components/ui/card.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | 3 | import { cn } from '@/lib/utils' 4 | 5 | function Card({ className, ...props }: React.ComponentProps<'div'>) { 6 | return ( 7 |
15 | ) 16 | } 17 | 18 | function CardHeader({ className, ...props }: React.ComponentProps<'div'>) { 19 | return ( 20 |
28 | ) 29 | } 30 | 31 | function CardTitle({ className, ...props }: React.ComponentProps<'div'>) { 32 | return ( 33 |
38 | ) 39 | } 40 | 41 | function CardDescription({ className, ...props }: React.ComponentProps<'div'>) { 42 | return ( 43 |
48 | ) 49 | } 50 | 51 | function CardAction({ className, ...props }: React.ComponentProps<'div'>) { 52 | return ( 53 |
61 | ) 62 | } 63 | 64 | function CardContent({ className, ...props }: React.ComponentProps<'div'>) { 65 | return ( 66 |
71 | ) 72 | } 73 | 74 | function CardFooter({ className, ...props }: React.ComponentProps<'div'>) { 75 | return ( 76 |
81 | ) 82 | } 83 | 84 | export { 85 | Card, 86 | CardHeader, 87 | CardFooter, 88 | CardTitle, 89 | CardAction, 90 | CardDescription, 91 | CardContent, 92 | } 93 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "my-v0-project", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "build": "next build", 7 | "dev": "next dev", 8 | "lint": "eslint .", 9 | "start": "next start" 10 | }, 11 | "dependencies": { 12 | "@hookform/resolvers": "^3.10.0", 13 | "@radix-ui/react-accordion": "1.2.2", 14 | "@radix-ui/react-alert-dialog": "1.1.4", 15 | "@radix-ui/react-aspect-ratio": "1.1.1", 16 | "@radix-ui/react-avatar": "1.1.2", 17 | "@radix-ui/react-checkbox": "1.1.3", 18 | "@radix-ui/react-collapsible": "1.1.2", 19 | "@radix-ui/react-context-menu": "2.2.4", 20 | "@radix-ui/react-dialog": "1.1.4", 21 | "@radix-ui/react-dropdown-menu": "2.1.4", 22 | "@radix-ui/react-hover-card": "1.1.4", 23 | "@radix-ui/react-label": "2.1.1", 24 | "@radix-ui/react-menubar": "1.1.4", 25 | "@radix-ui/react-navigation-menu": "1.2.3", 26 | "@radix-ui/react-popover": "1.1.4", 27 | "@radix-ui/react-progress": "1.1.1", 28 | "@radix-ui/react-radio-group": "1.2.2", 29 | "@radix-ui/react-scroll-area": "1.2.2", 30 | "@radix-ui/react-select": "2.1.4", 31 | "@radix-ui/react-separator": "1.1.1", 32 | "@radix-ui/react-slider": "1.2.2", 33 | "@radix-ui/react-slot": "1.1.1", 34 | "@radix-ui/react-switch": "1.1.2", 35 | "@radix-ui/react-tabs": "1.1.2", 36 | "@radix-ui/react-toast": "1.2.4", 37 | "@radix-ui/react-toggle": "1.1.1", 38 | "@radix-ui/react-toggle-group": "1.1.1", 39 | "@radix-ui/react-tooltip": "1.1.6", 40 | "@vercel/analytics": "1.3.1", 41 | "autoprefixer": "^10.4.20", 42 | "class-variance-authority": "^0.7.1", 43 | "clsx": "^2.1.1", 44 | "cmdk": "1.0.4", 45 | "date-fns": "4.1.0", 46 | "embla-carousel-react": "8.5.1", 47 | "input-otp": "1.4.1", 48 | "lucide-react": "^0.454.0", 49 | "next": "16.0.10", 50 | "next-themes": "^0.4.6", 51 | "react": "19.2.0", 52 | "react-day-picker": "9.8.0", 53 | "react-dom": "19.2.0", 54 | "react-hook-form": "^7.60.0", 55 | "react-resizable-panels": "^2.1.7", 56 | "recharts": "2.15.4", 57 | "sonner": "^1.7.4", 58 | "tailwind-merge": "^3.3.1", 59 | "tailwindcss-animate": "^1.0.7", 60 | "vaul": "^0.9.9", 61 | "zod": "3.25.76" 62 | }, 63 | "devDependencies": { 64 | "@tailwindcss/postcss": "^4.1.9", 65 | "@types/node": "^22", 66 | "@types/react": "^19", 67 | "@types/react-dom": "^19", 68 | "postcss": "^8.5", 69 | "tailwindcss": "^4.1.9", 70 | "tw-animate-css": "1.3.3", 71 | "typescript": "^5" 72 | } 73 | } -------------------------------------------------------------------------------- /public/placeholder-logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/placeholder.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /components/site-header.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import Link from "next/link" 4 | import { Moon, Sun, Globe, Menu } from "lucide-react" 5 | import { Button } from "@/components/ui/button" 6 | import { useTheme } from "@/components/theme-provider" 7 | import { useI18n } from "@/components/i18n-provider" 8 | import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@/components/ui/dropdown-menu" 9 | import { Sheet, SheetContent, SheetTrigger } from "@/components/ui/sheet" 10 | import { useState } from "react" 11 | 12 | export function SiteHeader() { 13 | const { theme, setTheme } = useTheme() 14 | const { locale, setLocale, t } = useI18n() 15 | const [mobileOpen, setMobileOpen] = useState(false) 16 | 17 | const navItems = [ 18 | { key: "nav.home", href: "/" }, 19 | { key: "nav.cases", href: "/cases" }, 20 | { key: "nav.knowledge", href: "/knowledge" }, 21 | { key: "nav.templates", href: "/templates" }, 22 | { key: "nav.about", href: "/about" }, 23 | ] 24 | 25 | return ( 26 |
27 |
28 | 29 |
30 | DX 31 |
32 | DXCP 33 | 34 | 35 | 42 | 43 |
44 | 45 | 46 | 49 | 50 | 51 | setLocale("zh")}>中文 {locale === "zh" && "✓"} 52 | setLocale("en")}>English {locale === "en" && "✓"} 53 | 54 | 55 | 56 | 59 | 60 | 61 | 62 | 65 | 66 | 67 | 79 | 80 | 81 |
82 |
83 |
84 | ) 85 | } 86 | -------------------------------------------------------------------------------- /app/templates/page.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import { Download, FileText } from "lucide-react" 4 | import { Button } from "@/components/ui/button" 5 | import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card" 6 | import { SiteHeader } from "@/components/site-header" 7 | import { SiteFooter } from "@/components/site-footer" 8 | import { useI18n } from "@/components/i18n-provider" 9 | 10 | export default function TemplatesPage() { 11 | const { t, locale } = useI18n() 12 | 13 | const templates = [ 14 | { 15 | titleKey: "templates.proposal", 16 | descKey: "templates.proposal.desc", 17 | file: "DXC-Project-Proposal.md", 18 | icon: FileText, 19 | }, 20 | { 21 | titleKey: "templates.pilot", 22 | descKey: "templates.pilot.desc", 23 | file: "DXC-Pilot-Plan.md", 24 | icon: FileText, 25 | }, 26 | ] 27 | 28 | const handleDownload = (filename: string) => { 29 | // In a real implementation, this would download the actual file 30 | // For now, we'll create a link to the GitHub repo 31 | window.open(`https://github.com/DXCP/website/tree/main/templates/${filename}`, "_blank") 32 | } 33 | 34 | return ( 35 |
36 | 37 | 38 |
39 |
40 |
41 |

{t("section.templates")}

42 |

43 | {locale === "zh" 44 | ? "下载 DXC 项目提案和试点计划模板,快速启动你的开发者体验中心" 45 | : "Download DXC project proposal and pilot plan templates to quickly start your Developer Experience Center"} 46 |

47 |
48 | 49 |
50 | {templates.map((template, index) => { 51 | const Icon = template.icon 52 | return ( 53 | 54 | 55 |
56 | 57 |
58 | {t(template.titleKey)} 59 | {t(template.descKey)} 60 |
61 | 62 | 66 | 67 |
68 | ) 69 | })} 70 |
71 | 72 | 73 | 74 | {locale === "zh" ? "贡献模板" : "Contribute Templates"} 75 | 76 | {locale === "zh" 77 | ? "我们欢迎社区贡献更多的 DXC 模板和最佳实践。通过 GitHub PR 提交您的模板。" 78 | : "We welcome community contributions of more DXC templates and best practices. Submit your templates via GitHub PR."} 79 | 80 | 81 | 82 | 87 | 88 | 89 |
90 |
91 | 92 | 93 |
94 | ) 95 | } 96 | -------------------------------------------------------------------------------- /data/cases.ts: -------------------------------------------------------------------------------- 1 | export type Case = { 2 | id: string 3 | title: { zh: string; en: string } 4 | company: { zh: string; en: string } 5 | description: { zh: string; en: string } 6 | tags: string[] 7 | metrics?: Array<{ 8 | value: string 9 | label: { zh: string; en: string } 10 | }> 11 | } 12 | 13 | export const cases: Case[] = [ 14 | { 15 | id: "1", 16 | title: { 17 | zh: "大型互联网公司的 DXC 实践", 18 | en: "Large Internet Company DXC Practice", 19 | }, 20 | company: { 21 | zh: "某互联网科技公司", 22 | en: "Tech Company A", 23 | }, 24 | description: { 25 | zh: "通过建立统一的开发者门户和 CI/CD 平台,该公司将新服务上线时间从 5 天缩短到 1 天,开发者满意度提升 35 分。", 26 | en: "By establishing a unified developer portal and CI/CD platform, the company reduced new service deployment time from 5 days to 1 day, increasing developer satisfaction by 35 points.", 27 | }, 28 | tags: ["CI/CD", "Portal"], 29 | metrics: [ 30 | { value: "80%", label: { zh: "时间节省", en: "Time Saved" } }, 31 | { value: "+35", label: { zh: "NPS 提升", en: "NPS Increase" } }, 32 | ], 33 | }, 34 | { 35 | id: "2", 36 | title: { 37 | zh: "金融科技的开发者体验转型", 38 | en: "Fintech Developer Experience Transformation", 39 | }, 40 | company: { 41 | zh: "某金融科技公司", 42 | en: "Fintech Company B", 43 | }, 44 | description: { 45 | zh: "实施 Onboarding Kit 和标准化服务模板后,新员工上手时间从 2 周缩短到 2 天,代码复用率提升 60%。", 46 | en: "After implementing Onboarding Kit and standardized service templates, new employee onboarding time was reduced from 2 weeks to 2 days, with code reuse increasing by 60%.", 47 | }, 48 | tags: ["Onboarding", "Templates"], 49 | metrics: [ 50 | { value: "85%", label: { zh: "上手时间缩短", en: "Onboarding Faster" } }, 51 | { value: "60%", label: { zh: "代码复用率", en: "Code Reuse" } }, 52 | ], 53 | }, 54 | { 55 | id: "3", 56 | title: { 57 | zh: "电商平台的可观测性实践", 58 | en: "E-commerce Platform Observability Practice", 59 | }, 60 | company: { 61 | zh: "某电商平台", 62 | en: "E-commerce Platform C", 63 | }, 64 | description: { 65 | zh: "通过统一的可观测性平台,平均故障恢复时间(MTTR)从 4 小时降至 45 分钟,变更失败率下降 40%。", 66 | en: "Through a unified observability platform, Mean Time To Recovery (MTTR) decreased from 4 hours to 45 minutes, with change failure rate dropping by 40%.", 67 | }, 68 | tags: ["Observability", "SRE"], 69 | metrics: [ 70 | { value: "81%", label: { zh: "MTTR 降低", en: "MTTR Reduction" } }, 71 | { value: "-40%", label: { zh: "失败率", en: "Failure Rate" } }, 72 | ], 73 | }, 74 | { 75 | id: "4", 76 | title: { 77 | zh: "SaaS 企业的研发效能提升", 78 | en: "SaaS Enterprise R&D Efficiency Boost", 79 | }, 80 | company: { 81 | zh: "某 SaaS 企业", 82 | en: "SaaS Company D", 83 | }, 84 | description: { 85 | zh: "建立开发者体验中心后,部署频率提升 200%,从每周 2 次提升到每天 6 次,交付周期缩短 50%。", 86 | en: "After establishing a Developer Experience Center, deployment frequency increased 200% from 2 times per week to 6 times per day, with lead time reduced by 50%.", 87 | }, 88 | tags: ["DevOps", "Metrics"], 89 | metrics: [ 90 | { value: "200%", label: { zh: "部署频率提升", en: "Deploy Freq Up" } }, 91 | { value: "-50%", label: { zh: "交付周期", en: "Lead Time" } }, 92 | ], 93 | }, 94 | { 95 | id: "5", 96 | title: { 97 | zh: "创业公司的快速成长之路", 98 | en: "Startup Fast Growth Path", 99 | }, 100 | company: { 101 | zh: "某创业公司", 102 | en: "Startup E", 103 | }, 104 | description: { 105 | zh: "在早期就建立 DXC 体系,使团队从 10 人扩展到 50 人时保持了高效协作,工程师离职率降低 70%。", 106 | en: "By establishing a DXC system early on, the team maintained efficient collaboration while scaling from 10 to 50 engineers, with engineer turnover reduced by 70%.", 107 | }, 108 | tags: ["Growth", "Culture"], 109 | metrics: [ 110 | { value: "5x", label: { zh: "团队规模", en: "Team Scale" } }, 111 | { value: "-70%", label: { zh: "离职率", en: "Turnover" } }, 112 | ], 113 | }, 114 | { 115 | id: "6", 116 | title: { 117 | zh: "传统企业的数字化转型", 118 | en: "Traditional Enterprise Digital Transformation", 119 | }, 120 | company: { 121 | zh: "某传统制造企业", 122 | en: "Manufacturing Company F", 123 | }, 124 | description: { 125 | zh: "通过 DXC 推动数字化转型,建立现代化研发体系,使软件交付速度提升 3 倍,质量问题减少 55%。", 126 | en: "By driving digital transformation through DXC and establishing a modern R&D system, software delivery speed increased 3x with quality issues reduced by 55%.", 127 | }, 128 | tags: ["Transformation", "Quality"], 129 | metrics: [ 130 | { value: "3x", label: { zh: "交付速度", en: "Delivery Speed" } }, 131 | { value: "-55%", label: { zh: "质量问题", en: "Quality Issues" } }, 132 | ], 133 | }, 134 | ] 135 | -------------------------------------------------------------------------------- /styles/globals.css: -------------------------------------------------------------------------------- 1 | @import 'tailwindcss'; 2 | @import 'tw-animate-css'; 3 | 4 | @custom-variant dark (&:is(.dark *)); 5 | 6 | :root { 7 | --background: oklch(1 0 0); 8 | --foreground: oklch(0.145 0 0); 9 | --card: oklch(1 0 0); 10 | --card-foreground: oklch(0.145 0 0); 11 | --popover: oklch(1 0 0); 12 | --popover-foreground: oklch(0.145 0 0); 13 | --primary: oklch(0.205 0 0); 14 | --primary-foreground: oklch(0.985 0 0); 15 | --secondary: oklch(0.97 0 0); 16 | --secondary-foreground: oklch(0.205 0 0); 17 | --muted: oklch(0.97 0 0); 18 | --muted-foreground: oklch(0.556 0 0); 19 | --accent: oklch(0.97 0 0); 20 | --accent-foreground: oklch(0.205 0 0); 21 | --destructive: oklch(0.577 0.245 27.325); 22 | --destructive-foreground: oklch(0.577 0.245 27.325); 23 | --border: oklch(0.922 0 0); 24 | --input: oklch(0.922 0 0); 25 | --ring: oklch(0.708 0 0); 26 | --chart-1: oklch(0.646 0.222 41.116); 27 | --chart-2: oklch(0.6 0.118 184.704); 28 | --chart-3: oklch(0.398 0.07 227.392); 29 | --chart-4: oklch(0.828 0.189 84.429); 30 | --chart-5: oklch(0.769 0.188 70.08); 31 | --radius: 0.625rem; 32 | --sidebar: oklch(0.985 0 0); 33 | --sidebar-foreground: oklch(0.145 0 0); 34 | --sidebar-primary: oklch(0.205 0 0); 35 | --sidebar-primary-foreground: oklch(0.985 0 0); 36 | --sidebar-accent: oklch(0.97 0 0); 37 | --sidebar-accent-foreground: oklch(0.205 0 0); 38 | --sidebar-border: oklch(0.922 0 0); 39 | --sidebar-ring: oklch(0.708 0 0); 40 | } 41 | 42 | .dark { 43 | --background: oklch(0.145 0 0); 44 | --foreground: oklch(0.985 0 0); 45 | --card: oklch(0.145 0 0); 46 | --card-foreground: oklch(0.985 0 0); 47 | --popover: oklch(0.145 0 0); 48 | --popover-foreground: oklch(0.985 0 0); 49 | --primary: oklch(0.985 0 0); 50 | --primary-foreground: oklch(0.205 0 0); 51 | --secondary: oklch(0.269 0 0); 52 | --secondary-foreground: oklch(0.985 0 0); 53 | --muted: oklch(0.269 0 0); 54 | --muted-foreground: oklch(0.708 0 0); 55 | --accent: oklch(0.269 0 0); 56 | --accent-foreground: oklch(0.985 0 0); 57 | --destructive: oklch(0.396 0.141 25.723); 58 | --destructive-foreground: oklch(0.637 0.237 25.331); 59 | --border: oklch(0.269 0 0); 60 | --input: oklch(0.269 0 0); 61 | --ring: oklch(0.439 0 0); 62 | --chart-1: oklch(0.488 0.243 264.376); 63 | --chart-2: oklch(0.696 0.17 162.48); 64 | --chart-3: oklch(0.769 0.188 70.08); 65 | --chart-4: oklch(0.627 0.265 303.9); 66 | --chart-5: oklch(0.645 0.246 16.439); 67 | --sidebar: oklch(0.205 0 0); 68 | --sidebar-foreground: oklch(0.985 0 0); 69 | --sidebar-primary: oklch(0.488 0.243 264.376); 70 | --sidebar-primary-foreground: oklch(0.985 0 0); 71 | --sidebar-accent: oklch(0.269 0 0); 72 | --sidebar-accent-foreground: oklch(0.985 0 0); 73 | --sidebar-border: oklch(0.269 0 0); 74 | --sidebar-ring: oklch(0.439 0 0); 75 | } 76 | 77 | @theme inline { 78 | --font-sans: 'Geist', 'Geist Fallback'; 79 | --font-mono: 'Geist Mono', 'Geist Mono Fallback'; 80 | --color-background: var(--background); 81 | --color-foreground: var(--foreground); 82 | --color-card: var(--card); 83 | --color-card-foreground: var(--card-foreground); 84 | --color-popover: var(--popover); 85 | --color-popover-foreground: var(--popover-foreground); 86 | --color-primary: var(--primary); 87 | --color-primary-foreground: var(--primary-foreground); 88 | --color-secondary: var(--secondary); 89 | --color-secondary-foreground: var(--secondary-foreground); 90 | --color-muted: var(--muted); 91 | --color-muted-foreground: var(--muted-foreground); 92 | --color-accent: var(--accent); 93 | --color-accent-foreground: var(--accent-foreground); 94 | --color-destructive: var(--destructive); 95 | --color-destructive-foreground: var(--destructive-foreground); 96 | --color-border: var(--border); 97 | --color-input: var(--input); 98 | --color-ring: var(--ring); 99 | --color-chart-1: var(--chart-1); 100 | --color-chart-2: var(--chart-2); 101 | --color-chart-3: var(--chart-3); 102 | --color-chart-4: var(--chart-4); 103 | --color-chart-5: var(--chart-5); 104 | --radius-sm: calc(var(--radius) - 4px); 105 | --radius-md: calc(var(--radius) - 2px); 106 | --radius-lg: var(--radius); 107 | --radius-xl: calc(var(--radius) + 4px); 108 | --color-sidebar: var(--sidebar); 109 | --color-sidebar-foreground: var(--sidebar-foreground); 110 | --color-sidebar-primary: var(--sidebar-primary); 111 | --color-sidebar-primary-foreground: var(--sidebar-primary-foreground); 112 | --color-sidebar-accent: var(--sidebar-accent); 113 | --color-sidebar-accent-foreground: var(--sidebar-accent-foreground); 114 | --color-sidebar-border: var(--sidebar-border); 115 | --color-sidebar-ring: var(--sidebar-ring); 116 | } 117 | 118 | @layer base { 119 | * { 120 | @apply border-border outline-ring/50; 121 | } 122 | body { 123 | @apply bg-background text-foreground; 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /app/globals.css: -------------------------------------------------------------------------------- 1 | @import "tailwindcss"; 2 | @import "tw-animate-css"; 3 | 4 | @custom-variant dark (&:is(.dark *)); 5 | 6 | :root { 7 | /* Light mode - Developer-focused blue and neutral palette */ 8 | --background: oklch(0.99 0 0); 9 | --foreground: oklch(0.15 0 0); 10 | --card: oklch(1 0 0); 11 | --card-foreground: oklch(0.15 0 0); 12 | --popover: oklch(1 0 0); 13 | --popover-foreground: oklch(0.15 0 0); 14 | --primary: oklch(0.45 0.15 250); /* Professional blue */ 15 | --primary-foreground: oklch(0.99 0 0); 16 | --secondary: oklch(0.96 0.01 250); 17 | --secondary-foreground: oklch(0.15 0 0); 18 | --muted: oklch(0.96 0.01 250); 19 | --muted-foreground: oklch(0.5 0.01 250); 20 | --accent: oklch(0.55 0.18 180); /* Teal accent */ 21 | --accent-foreground: oklch(0.99 0 0); 22 | --destructive: oklch(0.577 0.245 27.325); 23 | --destructive-foreground: oklch(0.99 0 0); 24 | --border: oklch(0.9 0.01 250); 25 | --input: oklch(0.9 0.01 250); 26 | --ring: oklch(0.45 0.15 250); 27 | --chart-1: oklch(0.45 0.15 250); 28 | --chart-2: oklch(0.55 0.18 180); 29 | --chart-3: oklch(0.65 0.15 140); 30 | --chart-4: oklch(0.6 0.2 30); 31 | --chart-5: oklch(0.55 0.15 280); 32 | --radius: 0.5rem; 33 | --sidebar: oklch(0.985 0 0); 34 | --sidebar-foreground: oklch(0.145 0 0); 35 | --sidebar-primary: oklch(0.205 0 0); 36 | --sidebar-primary-foreground: oklch(0.985 0 0); 37 | --sidebar-accent: oklch(0.97 0 0); 38 | --sidebar-accent-foreground: oklch(0.205 0 0); 39 | --sidebar-border: oklch(0.922 0 0); 40 | --sidebar-ring: oklch(0.708 0 0); 41 | } 42 | 43 | .dark { 44 | /* Dark mode - Deep blue/slate with teal accents */ 45 | --background: oklch(0.13 0.02 250); 46 | --foreground: oklch(0.95 0 0); 47 | --card: oklch(0.17 0.02 250); 48 | --card-foreground: oklch(0.95 0 0); 49 | --popover: oklch(0.17 0.02 250); 50 | --popover-foreground: oklch(0.95 0 0); 51 | --primary: oklch(0.65 0.2 180); /* Bright teal */ 52 | --primary-foreground: oklch(0.13 0.02 250); 53 | --secondary: oklch(0.22 0.02 250); 54 | --secondary-foreground: oklch(0.95 0 0); 55 | --muted: oklch(0.22 0.02 250); 56 | --muted-foreground: oklch(0.65 0.01 250); 57 | --accent: oklch(0.55 0.18 180); 58 | --accent-foreground: oklch(0.95 0 0); 59 | --destructive: oklch(0.55 0.25 27); 60 | --destructive-foreground: oklch(0.95 0 0); 61 | --border: oklch(0.25 0.02 250); 62 | --input: oklch(0.25 0.02 250); 63 | --ring: oklch(0.65 0.2 180); 64 | --chart-1: oklch(0.65 0.2 180); 65 | --chart-2: oklch(0.6 0.18 220); 66 | --chart-3: oklch(0.7 0.15 140); 67 | --chart-4: oklch(0.65 0.2 40); 68 | --chart-5: oklch(0.6 0.18 280); 69 | --sidebar: oklch(0.205 0 0); 70 | --sidebar-foreground: oklch(0.985 0 0); 71 | --sidebar-primary: oklch(0.488 0.243 264.376); 72 | --sidebar-primary-foreground: oklch(0.985 0 0); 73 | --sidebar-accent: oklch(0.269 0 0); 74 | --sidebar-accent-foreground: oklch(0.985 0 0); 75 | --sidebar-border: oklch(0.269 0 0); 76 | --sidebar-ring: oklch(0.439 0 0); 77 | } 78 | 79 | @theme inline { 80 | /* optional: --font-sans, --font-serif, --font-mono if they are applied in the layout.tsx */ 81 | --font-sans: "Geist", "Geist Fallback"; 82 | --font-mono: "Geist Mono", "Geist Mono Fallback"; 83 | --color-background: var(--background); 84 | --color-foreground: var(--foreground); 85 | --color-card: var(--card); 86 | --color-card-foreground: var(--card-foreground); 87 | --color-popover: var(--popover); 88 | --color-popover-foreground: var(--popover-foreground); 89 | --color-primary: var(--primary); 90 | --color-primary-foreground: var(--primary-foreground); 91 | --color-secondary: var(--secondary); 92 | --color-secondary-foreground: var(--secondary-foreground); 93 | --color-muted: var(--muted); 94 | --color-muted-foreground: var(--muted-foreground); 95 | --color-accent: var(--accent); 96 | --color-accent-foreground: var(--accent-foreground); 97 | --color-destructive: var(--destructive); 98 | --color-destructive-foreground: var(--destructive-foreground); 99 | --color-border: var(--border); 100 | --color-input: var(--input); 101 | --color-ring: var(--ring); 102 | --color-chart-1: var(--chart-1); 103 | --color-chart-2: var(--chart-2); 104 | --color-chart-3: var(--chart-3); 105 | --color-chart-4: var(--chart-4); 106 | --color-chart-5: var(--chart-5); 107 | --radius-sm: calc(var(--radius) - 4px); 108 | --radius-md: calc(var(--radius) - 2px); 109 | --radius-lg: var(--radius); 110 | --radius-xl: calc(var(--radius) + 4px); 111 | --color-sidebar: var(--sidebar); 112 | --color-sidebar-foreground: var(--sidebar-foreground); 113 | --color-sidebar-primary: var(--sidebar-primary); 114 | --color-sidebar-primary-foreground: var(--sidebar-primary-foreground); 115 | --color-sidebar-accent: var(--sidebar-accent); 116 | --color-sidebar-accent-foreground: var(--sidebar-accent-foreground); 117 | --color-sidebar-border: var(--sidebar-border); 118 | --color-sidebar-ring: var(--sidebar-ring); 119 | } 120 | 121 | @layer base { 122 | * { 123 | @apply border-border outline-ring/50; 124 | } 125 | body { 126 | @apply bg-background text-foreground; 127 | } 128 | .container { 129 | @apply mx-auto px-4 sm:px-6 lg:px-8; 130 | max-width: 1280px; 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /app/knowledge/page.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import { useState } from "react" 4 | import { Search } from "lucide-react" 5 | import { Input } from "@/components/ui/input" 6 | import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card" 7 | import { SiteHeader } from "@/components/site-header" 8 | import { SiteFooter } from "@/components/site-footer" 9 | import { useI18n } from "@/components/i18n-provider" 10 | import { knowledgeBase } from "@/data/knowledge" 11 | import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs" 12 | 13 | export default function KnowledgePage() { 14 | const { t, locale } = useI18n() 15 | const [searchQuery, setSearchQuery] = useState("") 16 | 17 | const categories = [ 18 | { key: "basics", labelKey: "knowledge.cat.basics" }, 19 | { key: "frameworks", labelKey: "knowledge.cat.frameworks" }, 20 | { key: "metrics", labelKey: "knowledge.cat.metrics" }, 21 | { key: "practices", labelKey: "knowledge.cat.practices" }, 22 | { key: "sharing", labelKey: "knowledge.cat.sharing" }, 23 | ] 24 | 25 | const filteredKnowledge = knowledgeBase.filter((item) => { 26 | if (!searchQuery) return true 27 | const title = locale === "zh" ? item.title.zh : item.title.en 28 | const desc = locale === "zh" ? item.description.zh : item.description.en 29 | return ( 30 | title.toLowerCase().includes(searchQuery.toLowerCase()) || desc.toLowerCase().includes(searchQuery.toLowerCase()) 31 | ) 32 | }) 33 | 34 | return ( 35 |
36 | 37 | 38 |
39 |
40 |
41 |

{t("section.knowledge")}

42 |

43 | {locale === "zh" 44 | ? "探索开发者体验的基础知识、框架、方法论和最佳实践" 45 | : "Explore developer experience fundamentals, frameworks, methodologies, and best practices"} 46 |

47 |
48 | 49 |
50 |
51 | 52 | setSearchQuery(e.target.value)} 56 | className="pl-10" 57 | /> 58 |
59 |
60 | 61 | 62 | 63 | {t("cases.filter.all")} 64 | {categories.map((cat) => ( 65 | 66 | {t(cat.labelKey)} 67 | 68 | ))} 69 | 70 | 71 | 72 | {categories.map((cat) => { 73 | const items = filteredKnowledge.filter((item) => item.category === cat.key) 74 | if (items.length === 0) return null 75 | return ( 76 |
77 |

{t(cat.labelKey)}

78 |
79 | {items.map((item) => ( 80 | 81 | 82 | {locale === "zh" ? item.title.zh : item.title.en} 83 | 84 | {locale === "zh" ? item.description.zh : item.description.en} 85 | 86 | 87 | {item.link && ( 88 | 89 | 95 | {locale === "zh" ? "了解更多 →" : "Learn more →"} 96 | 97 | 98 | )} 99 | 100 | ))} 101 |
102 |
103 | ) 104 | })} 105 |
106 | 107 | {categories.map((cat) => ( 108 | 109 |
110 | {filteredKnowledge 111 | .filter((item) => item.category === cat.key) 112 | .map((item) => ( 113 | 114 | 115 | {locale === "zh" ? item.title.zh : item.title.en} 116 | 117 | {locale === "zh" ? item.description.zh : item.description.en} 118 | 119 | 120 | {item.link && ( 121 | 122 | 128 | {locale === "zh" ? "了解更多 →" : "Learn more →"} 129 | 130 | 131 | )} 132 | 133 | ))} 134 |
135 |
136 | ))} 137 |
138 |
139 |
140 | 141 | 142 |
143 | ) 144 | } 145 | -------------------------------------------------------------------------------- /app/cases/page.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import { useState } from "react" 4 | import { Grid, List, Plus } from "lucide-react" 5 | import { Button } from "@/components/ui/button" 6 | import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card" 7 | import { SiteHeader } from "@/components/site-header" 8 | import { SiteFooter } from "@/components/site-footer" 9 | import { useI18n } from "@/components/i18n-provider" 10 | import { cases } from "@/data/cases" 11 | import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs" 12 | 13 | type ViewMode = "grid" | "list" 14 | 15 | export default function CasesPage() { 16 | const { t, locale } = useI18n() 17 | const [viewMode, setViewMode] = useState("grid") 18 | 19 | return ( 20 |
21 | 22 | 23 |
24 |
25 |
26 |
27 |

{t("section.cases")}

28 |

29 | {locale === "zh" ? "查看 DXCP 的成功实施案例" : "View successful DXCP implementation cases"} 30 |

31 |
32 |
33 | setViewMode(v as ViewMode)}> 34 | 35 | 36 | 37 | {t("cases.viewMode.grid")} 38 | 39 | 40 | 41 | {t("cases.viewMode.list")} 42 | 43 | 44 | 45 | 51 |
52 |
53 | 54 | {viewMode === "grid" ? ( 55 |
56 | {cases.map((caseItem) => ( 57 | 58 | 59 |
60 | {caseItem.tags.map((tag) => ( 61 | 65 | {tag} 66 | 67 | ))} 68 |
69 | {locale === "zh" ? caseItem.title.zh : caseItem.title.en} 70 | {locale === "zh" ? caseItem.company.zh : caseItem.company.en} 71 |
72 | 73 |

74 | {locale === "zh" ? caseItem.description.zh : caseItem.description.en} 75 |

76 | {caseItem.metrics && ( 77 |
78 | {caseItem.metrics.map((metric, idx) => ( 79 |
80 | {metric.value} 81 | 82 | {locale === "zh" ? metric.label.zh : metric.label.en} 83 | 84 |
85 | ))} 86 |
87 | )} 88 |
89 |
90 | ))} 91 |
92 | ) : ( 93 |
94 | {cases.map((caseItem) => ( 95 | 96 | 97 |
98 |
99 |
100 | {caseItem.tags.map((tag) => ( 101 | 105 | {tag} 106 | 107 | ))} 108 |
109 | 110 | {locale === "zh" ? caseItem.title.zh : caseItem.title.en} 111 | 112 | 113 | {locale === "zh" ? caseItem.company.zh : caseItem.company.en} 114 | 115 |
116 |
117 |
118 | 119 |

120 | {locale === "zh" ? caseItem.description.zh : caseItem.description.en} 121 |

122 | {caseItem.metrics && ( 123 |
124 | {caseItem.metrics.map((metric, idx) => ( 125 |
126 | {metric.value} 127 | 128 | {locale === "zh" ? metric.label.zh : metric.label.en} 129 | 130 |
131 | ))} 132 |
133 | )} 134 |
135 |
136 | ))} 137 |
138 | )} 139 |
140 |
141 | 142 | 143 |
144 | ) 145 | } 146 | -------------------------------------------------------------------------------- /app/page.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import Link from "next/link" 4 | import { ArrowRight, BookOpen, Code2, Container, Gauge, GitBranch, Rocket, Target, Users, Globe } from "lucide-react" 5 | import { Button } from "@/components/ui/button" 6 | import { Card, CardDescription, CardHeader, CardTitle } from "@/components/ui/card" 7 | import { SiteHeader } from "@/components/site-header" 8 | import { SiteFooter } from "@/components/site-footer" 9 | import { useI18n } from "@/components/i18n-provider" 10 | 11 | export default function HomePage() { 12 | const { t } = useI18n() 13 | 14 | const features = [ 15 | { 16 | icon: BookOpen, 17 | titleKey: "feature.portal.title", 18 | descKey: "feature.portal.desc", 19 | }, 20 | { 21 | icon: GitBranch, 22 | titleKey: "feature.cicd.title", 23 | descKey: "feature.cicd.desc", 24 | }, 25 | { 26 | icon: Container, 27 | titleKey: "feature.template.title", 28 | descKey: "feature.template.desc", 29 | }, 30 | { 31 | icon: Rocket, 32 | titleKey: "feature.onboarding.title", 33 | descKey: "feature.onboarding.desc", 34 | }, 35 | { 36 | icon: Gauge, 37 | titleKey: "feature.observability.title", 38 | descKey: "feature.observability.desc", 39 | }, 40 | { 41 | icon: Code2, 42 | titleKey: "feature.devenv.title", 43 | descKey: "feature.devenv.desc", 44 | }, 45 | ] 46 | 47 | const concepts = [ 48 | { 49 | icon: Globe, 50 | titleKey: "concept.dx.title", 51 | descKey: "concept.dx.desc", 52 | subtypes: "concept.dx.subtypes", 53 | }, 54 | { 55 | icon: Target, 56 | titleKey: "concept.dxc.title", 57 | descKey: "concept.dxc.desc", 58 | highlight: true, 59 | }, 60 | { 61 | icon: Users, 62 | titleKey: "concept.dxcp.title", 63 | descKey: "concept.dxcp.desc", 64 | highlight: true, 65 | }, 66 | ] 67 | 68 | return ( 69 |
70 | 71 | 72 |
73 | {/* Hero Section */} 74 |
75 |
76 |
77 | {t("hero.subtitle")} 78 |
79 |

80 | {t("hero.title")} 81 |

82 |

{t("hero.description")}

83 |
84 | 90 | 93 |
94 |
95 |
96 | 97 |
98 |
99 |

{t("section.concepts")}

100 |

{t("section.concepts.desc")}

101 |
102 |
103 | {concepts.map((concept, index) => { 104 | const Icon = concept.icon 105 | return ( 106 | 110 | 111 |
114 | 115 |
116 | {t(concept.titleKey)} 117 | {t(concept.descKey)} 118 | {concept.subtypes && ( 119 |
120 |

121 | {t("concept.dx.subtypes.label")} 122 |

123 |
124 | {["internal", "ecosystem", "opensource", "platform"].map((type) => ( 125 | 129 | {t(`concept.dx.subtypes.${type}`)} 130 | 131 | ))} 132 |
133 |
134 | )} 135 |
136 |
137 | ) 138 | })} 139 |
140 |
141 | 142 | {/* Features Section */} 143 |
144 |
145 |

{t("section.features")}

146 |
147 |
148 | {features.map((feature, index) => { 149 | const Icon = feature.icon 150 | return ( 151 | 152 | 153 |
154 | 155 |
156 | {t(feature.titleKey)} 157 | {t(feature.descKey)} 158 |
159 |
160 | ) 161 | })} 162 |
163 |
164 | 165 | {/* CTA Section */} 166 |
167 |
168 |

{t("section.templates")}

169 |

{t("templates.proposal.desc")}

170 | 176 |
177 |
178 |
179 | 180 | 181 |
182 | ) 183 | } 184 | -------------------------------------------------------------------------------- /app/about/page.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import { SiteHeader } from "@/components/site-header" 4 | import { SiteFooter } from "@/components/site-footer" 5 | import { useI18n } from "@/components/i18n-provider" 6 | import { Card, CardContent } from "@/components/ui/card" 7 | 8 | export default function AboutPage() { 9 | const { locale } = useI18n() 10 | 11 | return ( 12 |
13 | 14 | 15 |
16 |
17 |

{locale === "zh" ? "关于 DXCP" : "About DXCP"}

18 | 19 |
20 | {locale === "zh" ? ( 21 | <> 22 | 23 | 24 |

项目愿景

25 |
26 | "让每一位开发者都能以最少的摩擦、最高的效率创造价值。" 27 |
28 |
29 |
30 | 31 |

什么是 DXCP?

32 |

33 | DXCP(Developer Experience Center 34 | Program,开发者体验中心计划)是一个旨在建立统一、系统化、可持续改进的开发者体验平台的综合项目。 35 | 我们的目标是提高研发团队生产力、交付效率、系统质量与工程师满意度。 36 |

37 | 38 |

核心目标

39 |
    40 |
  • 41 | 战略目标:提升组织研发生产力与创新速度 42 |
  • 43 |
  • 44 | 战术目标:缩短交付周期、减少重复建设 45 |
  • 46 |
  • 47 | 操作目标:提高开发者满意度 48 |
  • 49 |
50 | 51 |

七大核心能力

52 |
    53 |
  1. 54 | Developer Portal(统一门户):服务目录、组件模板、API 文档、最佳实践 55 |
  2. 56 |
  3. 57 | CI/CD 自动化平台:模板化流水线、内置质量守则、自动安全扫描 58 |
  4. 59 |
  5. 60 | Service Template 库:标准微服务模板、内置监控、日志与部署配置 61 |
  6. 62 |
  7. 63 | Onboarding Kit:新员工极速上手包 64 |
  8. 65 |
  9. 66 | Observability as a Service:可观测性中心 67 |
  10. 68 |
  11. 69 | Dev Environment 即服务化:云端可复现开发环境 70 |
  12. 71 |
  13. 72 | DX 数据与反馈系统:自动采集 DevEx 指标 73 |
  14. 74 |
75 | 76 |

参与社区

77 |

78 | DXCP 是一个开放的社区项目。我们欢迎所有对开发者体验感兴趣的组织和个人参与贡献。 访问我们的{" "} 79 | 85 | GitHub 仓库 86 | {" "} 87 | 了解更多信息。 88 |

89 | 90 | ) : ( 91 | <> 92 | 93 | 94 |

Project Vision

95 |
96 | "Enable every developer to create value with minimal friction and maximum efficiency." 97 |
98 |
99 |
100 | 101 |

What is DXCP?

102 |

103 | DXCP (Developer Experience Center Program) is a comprehensive initiative aimed at building a unified, 104 | systematic, and continuously improving developer experience platform. Our goal is to enhance R&D team 105 | productivity, delivery efficiency, system quality, and engineer satisfaction. 106 |

107 | 108 |

Core Objectives

109 |
    110 |
  • 111 | Strategic: Enhance organizational R&D productivity and innovation speed 112 |
  • 113 |
  • 114 | Tactical: Shorten delivery cycles and reduce redundant work 115 |
  • 116 |
  • 117 | Operational: Improve developer satisfaction 118 |
  • 119 |
120 | 121 |

Seven Core Capabilities

122 |
    123 |
  1. 124 | Developer Portal: Service catalog, component templates, API docs, best practices 125 |
  2. 126 |
  3. 127 | CI/CD Automation Platform: Template pipelines, quality gates, security scanning 128 |
  4. 129 |
  5. 130 | Service Template Library: Standard microservice templates with monitoring and 131 | deployment 132 |
  6. 133 |
  7. 134 | Onboarding Kit: Fast onboarding package for new engineers 135 |
  8. 136 |
  9. 137 | Observability as a Service: Centralized observability center 138 |
  10. 139 |
  11. 140 | Dev Environment as a Service: Cloud-based reproducible dev environments 141 |
  12. 142 |
  13. 143 | DX Data & Feedback System: Automated DevEx metrics collection 144 |
  14. 145 |
146 | 147 |

Join the Community

148 |

149 | DXCP is an open community project. We welcome contributions from all organizations and individuals 150 | interested in developer experience. Visit our{" "} 151 | 157 | GitHub repository 158 | {" "} 159 | to learn more. 160 |

161 | 162 | )} 163 |
164 |
165 |
166 | 167 | 168 |
169 | ) 170 | } 171 | -------------------------------------------------------------------------------- /data/knowledge.ts: -------------------------------------------------------------------------------- 1 | export type KnowledgeItem = { 2 | id: string 3 | category: "basics" | "frameworks" | "metrics" | "practices" | "sharing" 4 | title: { zh: string; en: string } 5 | description: { zh: string; en: string } 6 | link?: string 7 | } 8 | 9 | export const knowledgeBase: KnowledgeItem[] = [ 10 | // Basics 11 | { 12 | id: "kb-1", 13 | category: "basics", 14 | title: { 15 | zh: "什么是开发者体验(DevEx)", 16 | en: "What is Developer Experience (DevEx)", 17 | }, 18 | description: { 19 | zh: "开发者体验是指开发人员在使用工具、框架、API 和平台时的整体感受和效率。良好的开发者体验能够提高生产力、降低认知负担并增加工作满意度。", 20 | en: "Developer Experience refers to the overall feeling and efficiency of developers when using tools, frameworks, APIs, and platforms. Good DevEx improves productivity, reduces cognitive load, and increases job satisfaction.", 21 | }, 22 | }, 23 | { 24 | id: "kb-2", 25 | category: "basics", 26 | title: { 27 | zh: "开发者体验的核心要素", 28 | en: "Core Elements of Developer Experience", 29 | }, 30 | description: { 31 | zh: "核心要素包括:工具链易用性、文档质量、上手速度、开发环境、自动化程度、反馈循环、协作效率等。每个要素都会显著影响开发者的日常工作体验。", 32 | en: "Core elements include: toolchain usability, documentation quality, onboarding speed, dev environment, automation level, feedback loops, and collaboration efficiency. Each significantly impacts developers' daily experience.", 33 | }, 34 | }, 35 | { 36 | id: "kb-3", 37 | category: "basics", 38 | title: { 39 | zh: "Platform Engineering vs DevEx", 40 | en: "Platform Engineering vs DevEx", 41 | }, 42 | description: { 43 | zh: "Platform Engineering 专注于构建自助式基础设施平台,而 DevEx 是更广泛的概念,涵盖开发者与技术栈的所有交互点。两者相辅相成。", 44 | en: "Platform Engineering focuses on building self-service infrastructure platforms, while DevEx is a broader concept covering all developer touchpoints with the tech stack. Both complement each other.", 45 | }, 46 | }, 47 | 48 | // Frameworks 49 | { 50 | id: "kb-4", 51 | category: "frameworks", 52 | title: { 53 | zh: "SPACE 框架", 54 | en: "SPACE Framework", 55 | }, 56 | description: { 57 | zh: "SPACE 框架从五个维度衡量开发者生产力:满意度与幸福感(Satisfaction)、绩效(Performance)、活动(Activity)、沟通与协作(Communication)、效率与流畅度(Efficiency)。", 58 | en: "SPACE Framework measures developer productivity across five dimensions: Satisfaction, Performance, Activity, Communication & Collaboration, and Efficiency & Flow.", 59 | }, 60 | link: "https://queue.acm.org/detail.cfm?id=3454124", 61 | }, 62 | { 63 | id: "kb-5", 64 | category: "frameworks", 65 | title: { 66 | zh: "DevEx Framework(DX Core 4)", 67 | en: "DevEx Framework (DX Core 4)", 68 | }, 69 | description: { 70 | zh: "DevEx Framework 识别影响开发者体验的四个核心维度:反馈循环(Feedback Loops)、认知负荷(Cognitive Load)、流畅状态(Flow State)和开发环境(Dev Environment)。", 71 | en: "DevEx Framework identifies four core dimensions affecting developer experience: Feedback Loops, Cognitive Load, Flow State, and Development Environment.", 72 | }, 73 | }, 74 | { 75 | id: "kb-6", 76 | category: "frameworks", 77 | title: { 78 | zh: "Team Topologies", 79 | en: "Team Topologies", 80 | }, 81 | description: { 82 | zh: "Team Topologies 提供了一套组织设计模式,包括 Stream-aligned teams、Enabling teams、Platform teams 等,帮助优化团队结构以提升开发者体验。", 83 | en: "Team Topologies provides organizational design patterns including Stream-aligned teams, Enabling teams, and Platform teams to optimize team structure for better DevEx.", 84 | }, 85 | }, 86 | 87 | // Metrics 88 | { 89 | id: "kb-7", 90 | category: "metrics", 91 | title: { 92 | zh: "DORA 指标", 93 | en: "DORA Metrics", 94 | }, 95 | description: { 96 | zh: "DORA(DevOps Research and Assessment)定义了四大关键指标:部署频率、变更前置时间、变更失败率和平均恢复时间,用于衡量软件交付性能。", 97 | en: "DORA defines four key metrics: Deployment Frequency, Lead Time for Changes, Change Failure Rate, and Mean Time to Recovery to measure software delivery performance.", 98 | }, 99 | link: "https://dora.dev/", 100 | }, 101 | { 102 | id: "kb-8", 103 | category: "metrics", 104 | title: { 105 | zh: "Developer NPS(Net Promoter Score)", 106 | en: "Developer NPS (Net Promoter Score)", 107 | }, 108 | description: { 109 | zh: 'Developer NPS 通过询问"你会向其他开发者推荐我们的平台/工具吗?"来量化开发者满意度,是衡量 DevEx 的重要指标。', 110 | en: 'Developer NPS quantifies developer satisfaction by asking "Would you recommend our platform/tools to other developers?" It\'s a key metric for measuring DevEx.', 111 | }, 112 | }, 113 | { 114 | id: "kb-9", 115 | category: "metrics", 116 | title: { 117 | zh: "开发者工作流指标", 118 | en: "Developer Workflow Metrics", 119 | }, 120 | description: { 121 | zh: "包括代码审查时间、CI/CD 执行时间、问题解决时间、文档查找时间等,这些指标反映开发者日常工作中的实际摩擦点。", 122 | en: "Includes code review time, CI/CD execution time, issue resolution time, documentation search time, etc. These metrics reflect actual friction points in daily developer work.", 123 | }, 124 | }, 125 | 126 | // Practices 127 | { 128 | id: "kb-10", 129 | category: "practices", 130 | title: { 131 | zh: "Inner Source 实践", 132 | en: "Inner Source Practices", 133 | }, 134 | description: { 135 | zh: "Inner Source 将开源协作模式应用于企业内部,鼓励跨团队代码共享和协作,显著提升代码复用率和开发者协作体验。", 136 | en: "Inner Source applies open source collaboration patterns within enterprises, encouraging cross-team code sharing and collaboration, significantly improving code reuse and developer collaboration experience.", 137 | }, 138 | }, 139 | { 140 | id: "kb-11", 141 | category: "practices", 142 | title: { 143 | zh: "Developer Portal 最佳实践", 144 | en: "Developer Portal Best Practices", 145 | }, 146 | description: { 147 | zh: 'Developer Portal 应提供统一的服务目录、API 文档、快速开始指南、模板库等,成为开发者的"一站式"工作入口。', 148 | en: 'Developer Portals should provide unified service catalogs, API documentation, quick start guides, template libraries, serving as a "one-stop" entry point for developers.', 149 | }, 150 | }, 151 | { 152 | id: "kb-12", 153 | category: "practices", 154 | title: { 155 | zh: "DevContainer 与云端开发环境", 156 | en: "DevContainer & Cloud Dev Environments", 157 | }, 158 | description: { 159 | zh: '使用 DevContainer 和云端开发环境可以实现"开箱即用"的开发体验,消除环境配置问题,让开发者专注于编码。', 160 | en: 'Using DevContainers and cloud dev environments enables "out-of-the-box" development experience, eliminating environment setup issues and allowing developers to focus on coding.', 161 | }, 162 | }, 163 | 164 | // Sharing 165 | { 166 | id: "kb-13", 167 | category: "sharing", 168 | title: { 169 | zh: "Spotify 的 Backstage", 170 | en: "Spotify's Backstage", 171 | }, 172 | description: { 173 | zh: "Spotify 开源的 Backstage 是业界领先的 Developer Portal 平台,提供服务目录、模板、文档、插件系统等完整功能。", 174 | en: "Spotify's open-source Backstage is an industry-leading Developer Portal platform, offering service catalog, templates, documentation, plugin system, and more.", 175 | }, 176 | link: "https://backstage.io/", 177 | }, 178 | { 179 | id: "kb-14", 180 | category: "sharing", 181 | title: { 182 | zh: "Netflix 的开发者生产力实践", 183 | en: "Netflix Developer Productivity Practices", 184 | }, 185 | description: { 186 | zh: "Netflix 通过建立专门的开发者生产力团队,持续优化工具链、自动化流程和开发环境,显著提升工程师效率和满意度。", 187 | en: "Netflix established dedicated developer productivity teams to continuously optimize toolchains, automate processes, and improve dev environments, significantly boosting engineer efficiency and satisfaction.", 188 | }, 189 | }, 190 | { 191 | id: "kb-15", 192 | category: "sharing", 193 | title: { 194 | zh: "Google 的 Developer Satisfaction(DSAT)", 195 | en: "Google's Developer Satisfaction (DSAT)", 196 | }, 197 | description: { 198 | zh: "Google 内部使用 DSAT 指标系统跟踪开发者对工具和流程的满意度,通过季度调研和持续改进维持高水平的开发者体验。", 199 | en: "Google uses the DSAT metric system to track developer satisfaction with tools and processes, maintaining high-level DevEx through quarterly surveys and continuous improvement.", 200 | }, 201 | }, 202 | { 203 | id: "kb-16", 204 | category: "sharing", 205 | title: { 206 | zh: "Shopify 的开发者加速器", 207 | en: "Shopify Developer Accelerator", 208 | }, 209 | description: { 210 | zh: "Shopify 的 Dev Accelerator 项目专注于消除开发过程中的摩擦,通过自动化、标准化和自助服务提升开发速度和体验。", 211 | en: "Shopify's Dev Accelerator project focuses on eliminating friction in the development process, improving development speed and experience through automation, standardization, and self-service.", 212 | }, 213 | }, 214 | ] 215 | -------------------------------------------------------------------------------- /components/i18n-provider.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import * as React from "react" 4 | 5 | export type Locale = "zh" | "en" 6 | 7 | type I18nContextType = { 8 | locale: Locale 9 | setLocale: (locale: Locale) => void 10 | t: (key: string) => string 11 | } 12 | 13 | const translations: Record> = { 14 | zh: { 15 | "nav.home": "首页", 16 | "nav.cases": "案例展示", 17 | "nav.knowledge": "知识库", 18 | "nav.templates": "模板下载", 19 | "nav.about": "关于", 20 | "hero.title": "开发者体验中心计划", 21 | "hero.subtitle": "Developer Experience Center Program", 22 | "hero.description": 23 | "建立统一、系统化、可持续改进的开发者体验平台,提升研发生产力、交付效率、系统质量与工程师满意度", 24 | "hero.getStarted": "开始使用", 25 | "hero.viewDocs": "查看文档", 26 | "section.concepts": "核心概念", 27 | "section.concepts.desc": "理解开发者体验的完整框架:从理念到组织到社区", 28 | "concept.dx.title": "DX / DevEx", 29 | "concept.dx.desc": 30 | "开发者体验(Developer Experience)是指开发者在使用工具、平台、API和服务时的整体感受和效率。良好的DX能显著提升生产力、降低学习成本、减少摩擦,最终提高工程师满意度和创新速度。", 31 | "concept.dx.subtypes.label": "不同范围划分", 32 | "concept.dx.subtypes.internal": "内部开发者体验", 33 | "concept.dx.subtypes.ecosystem": "开放生态开发者体验", 34 | "concept.dx.subtypes.opensource": "开源开发者体验", 35 | "concept.dx.subtypes.platform": "平台开发者体验", 36 | "concept.dxc.title": "DXC", 37 | "concept.dxc.desc": 38 | "开发者体验中心(Developer Experience Center)是一个统一的平台和组织,负责构建、维护和优化组织内的开发者工具链、流程和基础设施。DXC通过提供标准化模板、自动化工具和最佳实践,帮助开发团队提高效率和质量。", 39 | "concept.dxcp.title": "DXCP", 40 | "concept.dxcp.desc": 41 | "开发者体验中心计划(Developer Experience Center Program)是一个系统化的倡议和社区,旨在推广DXC理念、分享最佳实践、提供实施指南和建立行业标准。DXCP帮助组织从零开始建立自己的DXC,并持续改进开发者体验。", 42 | "section.features": "核心能力", 43 | "section.cases": "成功案例", 44 | "section.knowledge": "知识库", 45 | "section.templates": "下载模板", 46 | "feature.portal.title": "开发者门户", 47 | "feature.portal.desc": "统一的服务目录、组件模板、API文档与最佳实践", 48 | "feature.cicd.title": "CI/CD 自动化", 49 | "feature.cicd.desc": "模板化流水线、内置质量守则、自动安全扫描", 50 | "feature.template.title": "服务模板库", 51 | "feature.template.desc": "标准微服务模板、内置监控、日志与部署配置", 52 | "feature.onboarding.title": "快速上手", 53 | "feature.onboarding.desc": "新员工极速上手包,一键开发环境配置", 54 | "feature.observability.title": "可观测性", 55 | "feature.observability.desc": "标准日志、指标、追踪模板及统一告警", 56 | "feature.devenv.title": "开发环境", 57 | "feature.devenv.desc": "云端可复现开发环境,即服务化管理", 58 | "cases.viewMode.grid": "宫格视图", 59 | "cases.viewMode.list": "列表视图", 60 | "cases.filter.all": "全部", 61 | "cases.submitCase": "提交案例", 62 | "knowledge.search": "搜索知识库...", 63 | "knowledge.categories": "分类", 64 | "knowledge.cat.basics": "基础知识", 65 | "knowledge.cat.frameworks": "框架方法", 66 | "knowledge.cat.metrics": "度量标准", 67 | "knowledge.cat.practices": "最佳实践", 68 | "knowledge.cat.sharing": "大厂分享", 69 | "templates.proposal": "DXC 项目提案", 70 | "templates.proposal.desc": "包含项目概述、背景、目标、实施计划等完整提案模板", 71 | "templates.pilot": "DXC 试点计划", 72 | "templates.pilot.desc": "12周试点交付计划,包含周级里程碑和关键交付物", 73 | "templates.download": "下载模板", 74 | "footer.github": "GitHub", 75 | "footer.community": "社区", 76 | "footer.docs": "文档", 77 | "footer.copyright": "© 2025 DXCP. 保留所有权利。", 78 | }, 79 | en: { 80 | "nav.home": "Home", 81 | "nav.cases": "Cases", 82 | "nav.knowledge": "Knowledge", 83 | "nav.templates": "Templates", 84 | "nav.about": "About", 85 | "hero.title": "Developer Experience Center Program", 86 | "hero.subtitle": "DXCP", 87 | "hero.description": 88 | "Build a unified, systematic, and continuously improving developer experience platform to enhance R&D productivity, delivery efficiency, system quality, and engineer satisfaction", 89 | "hero.getStarted": "Get Started", 90 | "hero.viewDocs": "View Docs", 91 | "section.concepts": "Core Concepts", 92 | "section.concepts.desc": 93 | "Understanding the complete framework of developer experience: from philosophy to organization to community", 94 | "concept.dx.title": "DX / DevEx", 95 | "concept.dx.desc": 96 | "Developer Experience refers to the overall feeling and efficiency developers have when using tools, platforms, APIs, and services. Good DX significantly improves productivity, reduces learning costs, minimizes friction, and ultimately enhances engineer satisfaction and innovation speed.", 97 | "concept.dx.subtypes.label": "Scope Categories", 98 | "concept.dx.subtypes.internal": "Internal DX", 99 | "concept.dx.subtypes.ecosystem": "Ecosystem DX", 100 | "concept.dx.subtypes.opensource": "Open Source DX", 101 | "concept.dx.subtypes.platform": "Platform DX", 102 | "concept.dxc.title": "DXC", 103 | "concept.dxc.desc": 104 | "Developer Experience Center is a unified platform and organization responsible for building, maintaining, and optimizing the developer toolchain, processes, and infrastructure within an organization. DXC helps development teams improve efficiency and quality by providing standardized templates, automation tools, and best practices.", 105 | "concept.dxcp.title": "DXCP", 106 | "concept.dxcp.desc": 107 | "Developer Experience Center Program is a systematic initiative and community aimed at promoting DXC concepts, sharing best practices, providing implementation guides, and establishing industry standards. DXCP helps organizations build their own DXC from scratch and continuously improve developer experience.", 108 | "section.features": "Core Capabilities", 109 | "section.cases": "Success Stories", 110 | "section.knowledge": "Knowledge Base", 111 | "section.templates": "Download Templates", 112 | "feature.portal.title": "Developer Portal", 113 | "feature.portal.desc": "Unified service catalog, component templates, API docs and best practices", 114 | "feature.cicd.title": "CI/CD Automation", 115 | "feature.cicd.desc": "Template pipelines, built-in quality gates, automated security scanning", 116 | "feature.template.title": "Service Templates", 117 | "feature.template.desc": "Standard microservice templates with monitoring, logging, and deployment", 118 | "feature.onboarding.title": "Quick Onboarding", 119 | "feature.onboarding.desc": "Fast onboarding kit for new engineers with one-click dev environment", 120 | "feature.observability.title": "Observability", 121 | "feature.observability.desc": "Standard logging, metrics, tracing templates with unified alerting", 122 | "feature.devenv.title": "Dev Environment", 123 | "feature.devenv.desc": "Cloud-based reproducible dev environments as a service", 124 | "cases.viewMode.grid": "Grid View", 125 | "cases.viewMode.list": "List View", 126 | "cases.filter.all": "All", 127 | "cases.submitCase": "Submit Case", 128 | "knowledge.search": "Search knowledge base...", 129 | "knowledge.categories": "Categories", 130 | "knowledge.cat.basics": "Basics", 131 | "knowledge.cat.frameworks": "Frameworks", 132 | "knowledge.cat.metrics": "Metrics", 133 | "knowledge.cat.practices": "Best Practices", 134 | "knowledge.cat.sharing": "Industry Insights", 135 | "templates.proposal": "DXC Project Proposal", 136 | "templates.proposal.desc": 137 | "Complete proposal template with project overview, background, goals, and implementation plan", 138 | "templates.pilot": "DXC Pilot Plan", 139 | "templates.pilot.desc": "12-week pilot delivery plan with weekly milestones and key deliverables", 140 | "templates.download": "Download", 141 | "footer.github": "GitHub", 142 | "footer.community": "Community", 143 | "footer.docs": "Documentation", 144 | "footer.copyright": "© 2025 DXCP. All rights reserved.", 145 | }, 146 | } 147 | 148 | const defaultContextValue: I18nContextType = { 149 | locale: "zh", 150 | setLocale: () => {}, 151 | t: (key: string) => translations.zh[key] || key, 152 | } 153 | 154 | const I18nContext = React.createContext(defaultContextValue) 155 | 156 | export function I18nProvider({ children }: { children: React.ReactNode }) { 157 | const [locale, setLocale] = React.useState("zh") 158 | const [isClient, setIsClient] = React.useState(false) 159 | 160 | React.useEffect(() => { 161 | setIsClient(true) 162 | const stored = window.localStorage.getItem("locale") as Locale 163 | if (stored && (stored === "zh" || stored === "en")) { 164 | setLocale(stored) 165 | } 166 | }, []) 167 | 168 | const handleSetLocale = React.useCallback((newLocale: Locale) => { 169 | setLocale(newLocale) 170 | if (typeof window !== "undefined") { 171 | window.localStorage.setItem("locale", newLocale) 172 | } 173 | }, []) 174 | 175 | const t = React.useCallback( 176 | (key: string) => { 177 | return translations[locale][key] || key 178 | }, 179 | [locale], 180 | ) 181 | 182 | const value = React.useMemo( 183 | () => ({ 184 | locale, 185 | setLocale: handleSetLocale, 186 | t, 187 | }), 188 | [locale, handleSetLocale, t], 189 | ) 190 | 191 | return {children} 192 | } 193 | 194 | export function useI18n() { 195 | const context = React.useContext(I18nContext) 196 | return context 197 | } 198 | -------------------------------------------------------------------------------- /components/ui/dropdown-menu.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import * as React from 'react' 4 | import * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu' 5 | import { CheckIcon, ChevronRightIcon, CircleIcon } from 'lucide-react' 6 | 7 | import { cn } from '@/lib/utils' 8 | 9 | function DropdownMenu({ 10 | ...props 11 | }: React.ComponentProps) { 12 | return 13 | } 14 | 15 | function DropdownMenuPortal({ 16 | ...props 17 | }: React.ComponentProps) { 18 | return ( 19 | 20 | ) 21 | } 22 | 23 | function DropdownMenuTrigger({ 24 | ...props 25 | }: React.ComponentProps) { 26 | return ( 27 | 31 | ) 32 | } 33 | 34 | function DropdownMenuContent({ 35 | className, 36 | sideOffset = 4, 37 | ...props 38 | }: React.ComponentProps) { 39 | return ( 40 | 41 | 50 | 51 | ) 52 | } 53 | 54 | function DropdownMenuGroup({ 55 | ...props 56 | }: React.ComponentProps) { 57 | return ( 58 | 59 | ) 60 | } 61 | 62 | function DropdownMenuItem({ 63 | className, 64 | inset, 65 | variant = 'default', 66 | ...props 67 | }: React.ComponentProps & { 68 | inset?: boolean 69 | variant?: 'default' | 'destructive' 70 | }) { 71 | return ( 72 | 82 | ) 83 | } 84 | 85 | function DropdownMenuCheckboxItem({ 86 | className, 87 | children, 88 | checked, 89 | ...props 90 | }: React.ComponentProps) { 91 | return ( 92 | 101 | 102 | 103 | 104 | 105 | 106 | {children} 107 | 108 | ) 109 | } 110 | 111 | function DropdownMenuRadioGroup({ 112 | ...props 113 | }: React.ComponentProps) { 114 | return ( 115 | 119 | ) 120 | } 121 | 122 | function DropdownMenuRadioItem({ 123 | className, 124 | children, 125 | ...props 126 | }: React.ComponentProps) { 127 | return ( 128 | 136 | 137 | 138 | 139 | 140 | 141 | {children} 142 | 143 | ) 144 | } 145 | 146 | function DropdownMenuLabel({ 147 | className, 148 | inset, 149 | ...props 150 | }: React.ComponentProps & { 151 | inset?: boolean 152 | }) { 153 | return ( 154 | 163 | ) 164 | } 165 | 166 | function DropdownMenuSeparator({ 167 | className, 168 | ...props 169 | }: React.ComponentProps) { 170 | return ( 171 | 176 | ) 177 | } 178 | 179 | function DropdownMenuShortcut({ 180 | className, 181 | ...props 182 | }: React.ComponentProps<'span'>) { 183 | return ( 184 | 192 | ) 193 | } 194 | 195 | function DropdownMenuSub({ 196 | ...props 197 | }: React.ComponentProps) { 198 | return 199 | } 200 | 201 | function DropdownMenuSubTrigger({ 202 | className, 203 | inset, 204 | children, 205 | ...props 206 | }: React.ComponentProps & { 207 | inset?: boolean 208 | }) { 209 | return ( 210 | 219 | {children} 220 | 221 | 222 | ) 223 | } 224 | 225 | function DropdownMenuSubContent({ 226 | className, 227 | ...props 228 | }: React.ComponentProps) { 229 | return ( 230 | 238 | ) 239 | } 240 | 241 | export { 242 | DropdownMenu, 243 | DropdownMenuPortal, 244 | DropdownMenuTrigger, 245 | DropdownMenuContent, 246 | DropdownMenuGroup, 247 | DropdownMenuLabel, 248 | DropdownMenuItem, 249 | DropdownMenuCheckboxItem, 250 | DropdownMenuRadioGroup, 251 | DropdownMenuRadioItem, 252 | DropdownMenuSeparator, 253 | DropdownMenuShortcut, 254 | DropdownMenuSub, 255 | DropdownMenuSubTrigger, 256 | DropdownMenuSubContent, 257 | } 258 | --------------------------------------------------------------------------------