feat(contact): add multilingual contact pages and dynamic configurations
- Created `src/pages/contact.astro` and `src/pages/zh/contact.astro` to support multilingual contact functionality. - Introduced `contactIntents` and `contactMethods` dynamic configurations in `src/lib/data/contact.ts` for streamlined content management. - Designed responsive and localized layouts with enhanced UX for both languages.
This commit is contained in:
@@ -39,7 +39,7 @@ export default function Footer({ lang: propLang }: FooterProps) {
|
||||
className="text-sm text-primary font-medium text-center md:text-left"
|
||||
whileHover={{ scale: 1.01 }}
|
||||
>
|
||||
{lang === 'zh' ? '构建 AI 产品?寻找技术合伙人?联系我!' : 'Building AI Products? Need a Technical Co-founder? Contact me!'}
|
||||
{lang === 'zh' ? '正在招聘远程工程师或寻求项目合作?欢迎联系我。' : 'Hiring for a remote engineering role or looking for project collaboration? Let’s connect.'}
|
||||
</motion.p>
|
||||
</motion.div>
|
||||
<motion.p
|
||||
|
||||
@@ -5,7 +5,7 @@ import Container from "./ui/Container.tsx";
|
||||
import { useTranslations, getLocalizedPath } from "@/i18n/utils";
|
||||
import type { Lang } from "@/types/i18n";
|
||||
import { useState, useEffect } from "react";
|
||||
import { Menu, X, Home, Rocket, PenTool, Zap, Briefcase, User } from "lucide-react";
|
||||
import { Menu, X, Home, Rocket, PenTool, Zap, User, Wrench, Mail } from "lucide-react";
|
||||
import { defaultLang } from "@/i18n/ui";
|
||||
import { type GlassHeaderProps } from "@/types";
|
||||
import { motion } from "framer-motion";
|
||||
@@ -40,11 +40,12 @@ export default function GlassHeader({ lang: propLang }: GlassHeaderProps) {
|
||||
|
||||
const navItems = [
|
||||
{ key: 'nav.home', icon: Home, href: getLocalizedPath('/', lang) },
|
||||
{ key: 'nav.about', icon: User, href: getLocalizedPath('/about', lang) },
|
||||
{ key: 'nav.projects', icon: Rocket, href: getLocalizedPath('/projects', lang) },
|
||||
{ key: 'nav.blog', icon: PenTool, href: getLocalizedPath('/blog', lang) },
|
||||
{ key: 'nav.now', icon: Zap, href: getLocalizedPath('/now', lang) },
|
||||
{ key: 'nav.hire', icon: Briefcase, href: getLocalizedPath('/hire', lang) },
|
||||
{ key: 'nav.about', icon: User, href: getLocalizedPath('/about', lang) },
|
||||
{ key: 'nav.uses', icon: Wrench, href: getLocalizedPath('/uses', lang) },
|
||||
{ key: 'nav.contact', icon: Mail, href: getLocalizedPath('/contact', lang) },
|
||||
];
|
||||
|
||||
const isActive = (path: string) => {
|
||||
|
||||
@@ -17,7 +17,15 @@ export default function LanguageSwitcher({ lang: propLang }: LanguageSwitcherPro
|
||||
const [currentLang, setCurrentLang] = useState<Lang>(propLang || defaultLang);
|
||||
|
||||
useEffect(() => {
|
||||
if (typeof document !== 'undefined') {
|
||||
if (typeof window !== "undefined") {
|
||||
const pathLang = window.location.pathname.startsWith("/zh") ? "zh" : "en";
|
||||
if (pathLang !== currentLang) {
|
||||
setCurrentLang(pathLang);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof document !== "undefined") {
|
||||
const htmlLang = document.documentElement.lang as Lang;
|
||||
if (htmlLang && (!propLang || htmlLang !== currentLang)) {
|
||||
setCurrentLang(htmlLang);
|
||||
@@ -43,35 +51,14 @@ export default function LanguageSwitcher({ lang: propLang }: LanguageSwitcherPro
|
||||
const handleSelectLanguage = (languageOpt: typeof availableLanguages[0]) => {
|
||||
setSelectedLanguage(languageOpt);
|
||||
setIsOpen(false);
|
||||
if (typeof window !== 'undefined') {
|
||||
if (typeof window !== "undefined") {
|
||||
const currentPathname = window.location.pathname;
|
||||
const currentPathParts = currentPathname.split('/').filter(p => p);
|
||||
let basePath = '';
|
||||
const basePath = currentPathname.replace(/^\/(en|zh)(?=\/|$)/, "") || "/";
|
||||
const targetPath = getLocalizedPath(basePath, languageOpt.code);
|
||||
|
||||
if (currentPathParts.length > 0 && Object.keys(i18nLanguages).includes(currentPathParts[0])) {
|
||||
basePath = '/' + currentPathParts.slice(1).join('/');
|
||||
} else {
|
||||
basePath = currentPathname;
|
||||
}
|
||||
|
||||
if (!basePath.startsWith('/')) {
|
||||
basePath = '/' + basePath;
|
||||
}
|
||||
if (basePath === '//') basePath = '/';
|
||||
if (basePath === '') basePath = '/';
|
||||
|
||||
let newPath;
|
||||
if (languageOpt.code === defaultLang) {
|
||||
newPath = basePath;
|
||||
} else {
|
||||
newPath = `/${languageOpt.code}${basePath}`;
|
||||
}
|
||||
|
||||
newPath = newPath.replace(/\/\/+/g, '/');
|
||||
if (newPath === '') newPath = '/';
|
||||
|
||||
if (newPath !== currentPathname) {
|
||||
window.location.href = newPath;
|
||||
const normalize = (value: string) => value.replace(/\/+$/, "") || "/";
|
||||
if (normalize(targetPath) !== normalize(currentPathname)) {
|
||||
window.location.assign(targetPath);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -119,4 +106,4 @@ export default function LanguageSwitcher({ lang: propLang }: LanguageSwitcherPro
|
||||
</AnimatePresence>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,16 +9,15 @@ export const translations = {
|
||||
nav: {
|
||||
home: 'Home',
|
||||
about: 'About',
|
||||
services: 'Services',
|
||||
projects: 'Projects',
|
||||
blog: 'Blog',
|
||||
uses: 'Uses',
|
||||
contact: 'Contact',
|
||||
now: 'Now',
|
||||
hire: 'Hire',
|
||||
},
|
||||
site: {
|
||||
title: 'Joey Zhao - Full Stack Developer',
|
||||
description: 'Full Stack Developer specializing in React, Node.js, and modern web technologies',
|
||||
title: 'Joey Zhao - Senior Frontend Engineer',
|
||||
description: 'Senior Frontend Engineer building complex systems and AI-assisted products',
|
||||
},
|
||||
hero: {
|
||||
greeting: "Hello, I'm",
|
||||
@@ -70,7 +69,7 @@ export const translations = {
|
||||
},
|
||||
blog: {
|
||||
slogan: 'This is where innovative thinking meets complex problems.',
|
||||
backToList: 'Back to Blog',
|
||||
backToList: 'Back to Writing',
|
||||
},
|
||||
services: {
|
||||
title: 'What I Do',
|
||||
@@ -196,16 +195,15 @@ export const translations = {
|
||||
nav: {
|
||||
home: '首页',
|
||||
about: '关于',
|
||||
services: '服务',
|
||||
projects: '项目',
|
||||
blog: '博客',
|
||||
uses: '工具',
|
||||
contact: '联系',
|
||||
now: '现在',
|
||||
hire: '合作',
|
||||
},
|
||||
site: {
|
||||
title: 'Joey Zhao - 全栈开发者',
|
||||
description: '专注于 React、Node.js 和现代 Web 技术的全栈开发者',
|
||||
title: 'Joey Zhao - 资深前端工程师',
|
||||
description: '专注复杂系统与 AI 协作工程实践的资深前端工程师',
|
||||
},
|
||||
hero: {
|
||||
greeting: '你好,我是',
|
||||
@@ -257,7 +255,7 @@ export const translations = {
|
||||
},
|
||||
blog: {
|
||||
slogan: '这里是创新思维与复杂问题相遇的地方。',
|
||||
backToList: '返回博客列表',
|
||||
backToList: '返回写作列表',
|
||||
},
|
||||
services: {
|
||||
title: '我能做什么',
|
||||
|
||||
@@ -12,7 +12,7 @@ interface Props {
|
||||
}
|
||||
|
||||
const lang = Astro.currentLocale as Lang || defaultLang;
|
||||
const { title = "Rishikesh S - Portfolio", description = "My Portfolio" } =
|
||||
const { title = "Joey Zhao - Portfolio", description = "Engineering-focused personal website" } =
|
||||
Astro.props;
|
||||
const t = useTranslations(lang);
|
||||
---
|
||||
|
||||
44
src/lib/data/contact.ts
Normal file
44
src/lib/data/contact.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
import type { ContactIntent, ContactMethod } from '@/types';
|
||||
|
||||
export const contactIntents: ContactIntent[] = [
|
||||
{
|
||||
id: 'remote-role',
|
||||
title: {
|
||||
en: 'Remote Role',
|
||||
zh: '远程岗位沟通',
|
||||
},
|
||||
description: {
|
||||
en: 'Preferred for senior frontend/full-stack roles on complex products and systems.',
|
||||
zh: '优先沟通资深前端/全栈远程岗位,偏好复杂产品与系统场景。',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'project-collaboration',
|
||||
title: {
|
||||
en: 'Project Collaboration',
|
||||
zh: '项目合作咨询',
|
||||
},
|
||||
description: {
|
||||
en: 'Available for scoped project work involving architecture, delivery, and optimization.',
|
||||
zh: '可承接范围明确的项目合作,包括架构设计、交付落地与性能优化。',
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
export const contactMethods: ContactMethod[] = [
|
||||
{
|
||||
label: { en: 'Email', zh: '邮箱' },
|
||||
value: 'zhaoguiyang18@gmail.com',
|
||||
href: 'mailto:zhaoguiyang18@gmail.com',
|
||||
},
|
||||
{
|
||||
label: { en: 'GitHub', zh: 'GitHub' },
|
||||
value: 'github.com/zguiyang',
|
||||
href: 'https://github.com/zguiyang',
|
||||
},
|
||||
{
|
||||
label: { en: 'LinkedIn', zh: 'LinkedIn' },
|
||||
value: 'linkedin.com/in/zhaoguiyang',
|
||||
href: 'https://linkedin.com/in/zhaoguiyang',
|
||||
},
|
||||
];
|
||||
@@ -1,3 +1,5 @@
|
||||
export { personalInfo } from './personal-info';
|
||||
export { projects } from './projects';
|
||||
export { services } from './services';
|
||||
export { services } from './services';
|
||||
export { uses } from './uses';
|
||||
export { contactIntents, contactMethods } from './contact';
|
||||
|
||||
@@ -1,46 +1,47 @@
|
||||
import type { PersonalInfo } from '@/types';
|
||||
|
||||
export const personalInfo: PersonalInfo = {
|
||||
name: "Joey Zhao",
|
||||
location: "China",
|
||||
avatar: "https://avatars.githubusercontent.com/u/24975063?v=4",
|
||||
email: "zhaoguiyang18@gmail.com",
|
||||
github: "https://github.com/zguiyang",
|
||||
linkedin: "https://linkedin.com/in/zhaoguiyang",
|
||||
website: "https://zhaoguiyang.com",
|
||||
twitter: "https://twitter.com/zhaoguiyang",
|
||||
name: 'Joey Zhao',
|
||||
location: 'Hangzhou, China',
|
||||
avatar: 'https://avatars.githubusercontent.com/u/24975063?v=4',
|
||||
email: 'zhaoguiyang18@gmail.com',
|
||||
github: 'https://github.com/zguiyang',
|
||||
linkedin: 'https://linkedin.com/in/zhaoguiyang',
|
||||
website: 'https://zhaoguiyang.com',
|
||||
twitter: 'https://twitter.com/zhaoguiyang',
|
||||
position: {
|
||||
en: "Full-stack + AI Product Builder",
|
||||
zh: "全栈 + AI 产品构建者"
|
||||
en: 'Senior Frontend Engineer · Full-stack Developer',
|
||||
zh: '资深前端工程师 · 全栈开发者',
|
||||
},
|
||||
description: {
|
||||
en: "Building AI-powered products and turning ambitious ideas into reality. Currently focused on Elynd - an open AI workspace for builders.",
|
||||
zh: "构建 AI 驱动产品,将雄心勃勃的想法变为现实。目前专注于 Elynd - 一个面向构建者的开放 AI 工作空间。"
|
||||
en: '8 years building enterprise systems, financial platforms, and blockchain infrastructure. Focused on frontend architecture, complex system design, and AI-assisted engineering workflows.',
|
||||
zh: '8 年企业系统、金融平台与区块链基础设施研发经验,专注前端架构、复杂系统设计与 AI 协作工程化实践。',
|
||||
},
|
||||
about: {
|
||||
en: [
|
||||
"I started my programming journey in 2016 when I accidentally encountered programming. This opportunity became the catalyst for my coding adventure, and I fell in love with programming during my subsequent self-learning journey.",
|
||||
"I enjoy creating websites and applications with code and sharing them with users. The sense of achievement I get from this process makes me increasingly fascinated. My dream is to become a lifelong coder.",
|
||||
"I love reading, traveling (especially road trips), enjoying various cuisines (particularly hot pot), and playing mobile games like League of Legends, Honor of Kings, and PUBG Mobile."
|
||||
'I build reliable web systems with strong architecture discipline and delivery ownership.',
|
||||
'My core experience comes from enterprise, finance, and blockchain product domains.',
|
||||
'I work effectively in remote-first teams with clear async communication and stable execution cadence.',
|
||||
],
|
||||
zh: [
|
||||
"在2016年一次偶然的机会中接触到了编程,以此为契机,开始了我的编程之旅,并在后续的自学之旅中爱上了编程。",
|
||||
"我喜欢用代码编写出一个个的网页、应用,分享给用户使用,这其中获得的成就感让我愈发着迷。梦想是成为一名终身编码者。",
|
||||
"热爱生活、阅读以及编程,寻找远程工作的机会,探索自由职业的可能性。喜欢自驾,去追寻那些美丽的风景,尝试新的事物。"
|
||||
]
|
||||
'我专注构建稳定、可维护、可扩展的 Web 系统,并对交付结果负责。',
|
||||
'核心经验来自企业级、金融科技与区块链产品场景。',
|
||||
'擅长远程协作与异步沟通,能够保持稳定节奏持续交付。',
|
||||
],
|
||||
},
|
||||
stats: {
|
||||
repositories: 152,
|
||||
commits: "42K",
|
||||
contributions: 87
|
||||
commits: '42K',
|
||||
contributions: 87,
|
||||
},
|
||||
skills: {
|
||||
frontend: ["HTML", "CSS", "JavaScript", "Vue.js", "React.js", "TypeScript"],
|
||||
backend: ["Node.js"],
|
||||
database: ["MySQL", "MongoDB"],
|
||||
devops: ["Linux", "Git", "Docker", "Nginx", "Apache"],
|
||||
mobile: ["React Native", "Flutter"]
|
||||
frontend: ['TypeScript', 'React', 'Vue', 'Astro'],
|
||||
backend: ['Node.js', 'NestJS', 'Fastify'],
|
||||
database: ['PostgreSQL', 'MySQL', 'MongoDB'],
|
||||
devops: ['Linux', 'Git', 'Docker', 'Nginx'],
|
||||
mobile: ['React Native', 'Flutter'],
|
||||
},
|
||||
terminal: {
|
||||
username: "joy@dev-workspace"
|
||||
}
|
||||
};
|
||||
username: 'joy@engineer',
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,207 +1,258 @@
|
||||
import type { Project } from '@/types';
|
||||
|
||||
const sharedCases: Project[] = [
|
||||
{
|
||||
id: 'digital-securities-platform',
|
||||
featured: true,
|
||||
type: 'product',
|
||||
status: 'completed',
|
||||
role: 'Frontend Architecture Lead',
|
||||
impact: 'Delivered a stable trading platform for multi-role financial institutions',
|
||||
systemType: 'Financial blockchain trading platform',
|
||||
context: 'A digital securities trading system for regulated institutions and operations teams.',
|
||||
title: 'Digital Securities Trading Platform',
|
||||
icon: 'Landmark',
|
||||
color: 'blue',
|
||||
image: {
|
||||
bg: 'from-blue-500/20 to-indigo-600/20',
|
||||
hover: 'from-blue-500/30 to-indigo-600/30',
|
||||
text: 'text-blue-500',
|
||||
},
|
||||
challenges: [
|
||||
'Complex multi-role permission boundaries',
|
||||
'High reliability requirements for trading operations',
|
||||
'Integration with blockchain wallet services',
|
||||
],
|
||||
responsibilities: [
|
||||
'Designed frontend module architecture and domain boundaries',
|
||||
'Led user management and permission refactor',
|
||||
'Integrated bank login and compliance-related flows',
|
||||
],
|
||||
outcomes: [
|
||||
'Improved operational stability with reliable release rhythm',
|
||||
'Reduced permission-related defects through clearer boundaries',
|
||||
'Supported business launch for institutional users',
|
||||
],
|
||||
description: [
|
||||
'A digital asset trading system supporting regulated workflows.',
|
||||
'Built for role-based operations, auditing, and risk-sensitive processes.',
|
||||
],
|
||||
tech: ['TypeScript', 'React', 'Node.js', 'Permission System'],
|
||||
link: '#',
|
||||
},
|
||||
{
|
||||
id: 'baas-platform',
|
||||
featured: true,
|
||||
type: 'product',
|
||||
status: 'completed',
|
||||
role: 'Senior Frontend Engineer',
|
||||
impact: 'Enabled enterprise clients to manage blockchain services efficiently',
|
||||
systemType: 'Blockchain BaaS platform',
|
||||
context: 'Enterprise-grade blockchain service platform for chain management and operations.',
|
||||
title: 'Blockchain BaaS Platform',
|
||||
icon: 'Blocks',
|
||||
color: 'violet',
|
||||
image: {
|
||||
bg: 'from-violet-500/20 to-fuchsia-500/20',
|
||||
hover: 'from-violet-500/30 to-fuchsia-500/30',
|
||||
text: 'text-violet-500',
|
||||
},
|
||||
challenges: [
|
||||
'High-density operational console information',
|
||||
'Complex asynchronous task status visibility',
|
||||
'Multi-environment configuration consistency',
|
||||
],
|
||||
responsibilities: [
|
||||
'Built scalable admin UI architecture for service management',
|
||||
'Implemented observable task state flows and feedback loops',
|
||||
'Drove reusable component patterns across modules',
|
||||
],
|
||||
outcomes: [
|
||||
'Improved operation efficiency of chain service workflows',
|
||||
'Reduced repetitive UI implementation with reusable patterns',
|
||||
'Increased delivery speed in subsequent modules',
|
||||
],
|
||||
description: [
|
||||
'A platform for enterprise blockchain network and service operations.',
|
||||
'Focused on maintainability and operational efficiency.',
|
||||
],
|
||||
tech: ['React', 'TypeScript', 'Ant Design', 'WebSocket'],
|
||||
link: '#',
|
||||
},
|
||||
{
|
||||
id: 'supply-chain-finance',
|
||||
featured: true,
|
||||
type: 'client',
|
||||
status: 'completed',
|
||||
role: 'Full-stack Engineer',
|
||||
impact: 'Supported finance workflows for upstream and downstream participants',
|
||||
systemType: 'Supply chain finance platform',
|
||||
context: 'A collaborative system connecting enterprises, finance teams, and partner organizations.',
|
||||
title: 'Supply Chain Finance Platform',
|
||||
icon: 'Network',
|
||||
color: 'emerald',
|
||||
image: {
|
||||
bg: 'from-emerald-500/20 to-teal-500/20',
|
||||
hover: 'from-emerald-500/30 to-teal-500/30',
|
||||
text: 'text-emerald-500',
|
||||
},
|
||||
challenges: [
|
||||
'Long business流程 with many states and edge cases',
|
||||
'Cross-role data visibility requirements',
|
||||
'Need for high traceability in approvals and operations',
|
||||
],
|
||||
responsibilities: [
|
||||
'Implemented core workflow modules and state transitions',
|
||||
'Designed role-specific views and permission controls',
|
||||
'Optimized key pages for faster operational handling',
|
||||
],
|
||||
outcomes: [
|
||||
'Improved workflow transparency across participants',
|
||||
'Reduced process handover friction in finance operations',
|
||||
'Enabled stable iterative delivery for business expansion',
|
||||
],
|
||||
description: [
|
||||
'A multi-role financial workflow system for supply chain participants.',
|
||||
'Focused on process clarity, reliability, and operations efficiency.',
|
||||
],
|
||||
tech: ['TypeScript', 'React', 'Node.js', 'PostgreSQL'],
|
||||
link: '#',
|
||||
},
|
||||
{
|
||||
id: 'smart-park-system',
|
||||
featured: false,
|
||||
type: 'client',
|
||||
status: 'completed',
|
||||
role: 'Frontend Engineer',
|
||||
impact: 'Integrated business modules for industrial operations management',
|
||||
systemType: 'Industrial smart park management system',
|
||||
context: 'A digital management system for operations, resources, and service workflows in industrial parks.',
|
||||
title: 'Industrial Smart Park Management System',
|
||||
icon: 'Building2',
|
||||
color: 'orange',
|
||||
image: {
|
||||
bg: 'from-orange-500/20 to-amber-500/20',
|
||||
hover: 'from-orange-500/30 to-amber-500/30',
|
||||
text: 'text-orange-500',
|
||||
},
|
||||
challenges: [
|
||||
'Many business modules with different usage patterns',
|
||||
'Dashboard readability for operation teams',
|
||||
'Need for maintainable long-term module evolution',
|
||||
],
|
||||
responsibilities: [
|
||||
'Developed key modules and unified UI interaction patterns',
|
||||
'Improved dashboard information hierarchy and readability',
|
||||
'Collaborated with backend and product teams for iterative delivery',
|
||||
],
|
||||
outcomes: [
|
||||
'Improved operator experience in daily management tasks',
|
||||
'Reduced UI inconsistency across modules',
|
||||
'Supported long-term maintainability for continued delivery',
|
||||
],
|
||||
description: [
|
||||
'A comprehensive system for industrial park operations and services.',
|
||||
'Designed for clarity, consistency, and scalable module growth.',
|
||||
],
|
||||
tech: ['React', 'TypeScript', 'ECharts', 'REST API'],
|
||||
link: '#',
|
||||
},
|
||||
{
|
||||
id: 'elynd',
|
||||
featured: false,
|
||||
type: 'experiment',
|
||||
status: 'building',
|
||||
role: 'Founder & Developer',
|
||||
impact: 'Exploring AI-assisted development workflows in production-like scenarios',
|
||||
systemType: 'AI-assisted workspace',
|
||||
context: 'An open workspace product exploring practical AI collaboration in software development.',
|
||||
title: 'Elynd',
|
||||
icon: 'Zap',
|
||||
color: 'purple',
|
||||
image: {
|
||||
bg: 'from-purple-500/20 to-blue-500/20',
|
||||
hover: 'from-purple-500/30 to-blue-500/30',
|
||||
text: 'text-purple-500',
|
||||
},
|
||||
challenges: ['Designing useful AI workflows without adding process burden'],
|
||||
responsibilities: ['Product exploration and end-to-end implementation'],
|
||||
outcomes: ['Validated several practical AI-assisted development patterns'],
|
||||
description: [
|
||||
'An open AI workspace for builders.',
|
||||
'Used as an R&D track, not the primary professional narrative.',
|
||||
],
|
||||
tech: ['TypeScript', 'React', 'AI Workflow', 'Open Source'],
|
||||
link: '#',
|
||||
},
|
||||
];
|
||||
|
||||
export const projects = {
|
||||
en: [
|
||||
{
|
||||
id: "elynd",
|
||||
featured: true,
|
||||
type: "product",
|
||||
status: "building",
|
||||
role: "Founder & Lead Developer",
|
||||
impact: "Building an open AI workspace for builders",
|
||||
title: "Elynd",
|
||||
icon: "Zap",
|
||||
color: "purple",
|
||||
image: {
|
||||
bg: "from-purple-500/20 to-blue-500/20",
|
||||
hover: "from-purple-500/30 to-blue-500/30",
|
||||
text: "text-purple-400",
|
||||
},
|
||||
description: [
|
||||
"An open AI workspace for builders to work smarter.",
|
||||
"Built with AI-first principles, making AI your co-builder.",
|
||||
"Fully open source and self-hostable.",
|
||||
"Currently in active development with early access coming soon.",
|
||||
],
|
||||
tech: ["TypeScript", "React", "AI/ML", "Open Source"],
|
||||
link: "#",
|
||||
links: {
|
||||
demo: "#",
|
||||
github: "https://github.com",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "taskify",
|
||||
type: "client",
|
||||
status: "completed",
|
||||
role: "Lead Developer",
|
||||
impact: "Helped startup launch MVP in 3 months",
|
||||
title: "Taskify App",
|
||||
tag: "business",
|
||||
icon: "Smartphone",
|
||||
color: "purple",
|
||||
image: {
|
||||
bg: "from-purple-500/20 to-purple-600/20",
|
||||
hover: "from-purple-500/20 to-purple-600/20",
|
||||
text: "text-purple-400",
|
||||
},
|
||||
description: [
|
||||
"A comprehensive task management application with drag-and-drop functionality.",
|
||||
"Built with React, TypeScript, and Tailwind CSS using modern development approaches.",
|
||||
"Real-time collaboration through WebSocket integration for instant updates.",
|
||||
"Advanced task filtering, sorting, and project management capabilities.",
|
||||
],
|
||||
tech: ["React", "Node.js", "MongoDB"],
|
||||
link: "#",
|
||||
},
|
||||
{
|
||||
id: "eshop",
|
||||
type: "client",
|
||||
status: "completed",
|
||||
role: "Full-stack Developer",
|
||||
impact: "E-commerce platform handling $100K+ monthly revenue",
|
||||
title: "E-Shop Platform",
|
||||
tag: "business",
|
||||
icon: "ShoppingCart",
|
||||
color: "green",
|
||||
image: {
|
||||
bg: "from-green-500/20 to-green-600/20",
|
||||
hover: "from-green-500/20 to-green-600/20",
|
||||
text: "text-green-400",
|
||||
},
|
||||
description: [
|
||||
"Scalable e-commerce platform built with Next.js, Stripe payments, and TailwindCSS.",
|
||||
"Mobile-first responsive design for optimal user experience across devices.",
|
||||
"Integrated payment processing with Stripe for secure transactions.",
|
||||
"Admin dashboard for inventory management and order processing.",
|
||||
],
|
||||
tech: ["Next.js", "Stripe", "TailwindCSS"],
|
||||
link: "#",
|
||||
},
|
||||
{
|
||||
id: "portfolio",
|
||||
type: "experiment",
|
||||
status: "completed",
|
||||
role: "Creator",
|
||||
impact: "Personal brand and portfolio showcasing",
|
||||
title: "Portfolio Site",
|
||||
tag: "portfolio",
|
||||
icon: "Globe",
|
||||
color: "purple",
|
||||
image: {
|
||||
bg: "from-purple-500/20 to-purple-600/20",
|
||||
hover: "from-purple-500/20 to-purple-600/20",
|
||||
text: "text-purple-400",
|
||||
},
|
||||
description: [
|
||||
"Personal portfolio showcasing my work, built with HTML, TailwindCSS, and Alpine.js.",
|
||||
"Responsive design with dark mode support and smooth animations.",
|
||||
"Optimized for performance with minimal JavaScript and efficient CSS.",
|
||||
"Integrated blog section with markdown support for content management.",
|
||||
],
|
||||
tech: ["HTML", "TailwindCSS", "Alpine.js"],
|
||||
link: "#",
|
||||
},
|
||||
],
|
||||
en: sharedCases,
|
||||
zh: [
|
||||
{
|
||||
id: "elynd",
|
||||
featured: true,
|
||||
type: "product",
|
||||
status: "building",
|
||||
role: "创始人 & 首席开发者",
|
||||
impact: "构建面向构建者的开放 AI 工作空间",
|
||||
title: "Elynd",
|
||||
icon: "Zap",
|
||||
color: "purple",
|
||||
image: {
|
||||
bg: "from-purple-500/20 to-blue-500/20",
|
||||
hover: "from-purple-500/30 to-blue-500/30",
|
||||
text: "text-purple-400",
|
||||
},
|
||||
description: [
|
||||
"一个面向构建者的开放 AI 工作空间。",
|
||||
"从 AI 第一性原理出发,让 AI 成为你的共同构建者。",
|
||||
"完全开源,可自托管。",
|
||||
"目前正在积极开发中,早期访问即将推出。",
|
||||
],
|
||||
tech: ["TypeScript", "React", "AI/ML", "开源"],
|
||||
link: "#",
|
||||
links: {
|
||||
demo: "#",
|
||||
github: "https://github.com",
|
||||
},
|
||||
...sharedCases[0],
|
||||
role: '前端架构负责人',
|
||||
impact: '为多角色金融机构交付稳定的交易平台',
|
||||
systemType: '数字证券金融区块链交易平台',
|
||||
context: '面向受监管机构与运营团队的数字证券交易系统。',
|
||||
title: '数字证券交易平台',
|
||||
challenges: ['多角色复杂权限边界', '交易场景下的高稳定性要求', '区块链钱包服务集成'],
|
||||
responsibilities: ['设计前端模块化架构与领域边界', '主导用户与权限体系重构', '接入银行登录与合规相关流程'],
|
||||
outcomes: ['提升系统稳定性与发版可控性', '通过边界治理降低权限类缺陷', '支撑机构端业务落地'],
|
||||
description: ['支持受监管流程的数字资产交易系统。', '强调角色权限、审计可追踪和风险敏感流程。'],
|
||||
tech: ['TypeScript', 'React', 'Node.js', '权限系统'],
|
||||
},
|
||||
{
|
||||
id: "taskify",
|
||||
type: "client",
|
||||
status: "completed",
|
||||
role: "首席开发者",
|
||||
impact: "帮助创业公司在3个月内推出 MVP",
|
||||
title: "Taskify 应用",
|
||||
tag: "business",
|
||||
icon: "Smartphone",
|
||||
color: "purple",
|
||||
image: {
|
||||
bg: "from-purple-500/20 to-purple-600/20",
|
||||
hover: "from-purple-500/20 to-purple-600/20",
|
||||
text: "text-purple-400",
|
||||
},
|
||||
description: [
|
||||
"一个全面的任务管理应用,具有拖放功能。",
|
||||
"使用React、TypeScript和Tailwind CSS构建,采用现代开发方法。",
|
||||
"通过WebSocket集成实现实时协作功能,实现即时更新。",
|
||||
"高级任务筛选、排序和项目管理功能。",
|
||||
],
|
||||
tech: ["React", "Node.js", "MongoDB"],
|
||||
link: "#",
|
||||
...sharedCases[1],
|
||||
role: '资深前端工程师',
|
||||
impact: '帮助企业客户高效管理区块链服务',
|
||||
systemType: '区块链 BaaS 平台',
|
||||
context: '面向企业的链服务管理与运维平台。',
|
||||
title: '区块链 BaaS 平台',
|
||||
challenges: ['高密度运维信息的可读性', '异步任务状态可观察性', '多环境配置一致性'],
|
||||
responsibilities: ['构建可扩展的管理端前端架构', '实现可追踪任务状态反馈链路', '推动跨模块可复用组件方案'],
|
||||
outcomes: ['提升链服务运维效率', '减少重复 UI 开发成本', '提高后续模块迭代速度'],
|
||||
description: ['面向企业区块链网络与服务运维的平台。', '重点优化可维护性与操作效率。'],
|
||||
tech: ['React', 'TypeScript', 'Ant Design', 'WebSocket'],
|
||||
},
|
||||
{
|
||||
id: "eshop",
|
||||
type: "client",
|
||||
status: "completed",
|
||||
role: "全栈开发者",
|
||||
impact: "电商平台月营收超过 10 万美元",
|
||||
title: "电商平台",
|
||||
tag: "business",
|
||||
icon: "ShoppingCart",
|
||||
color: "green",
|
||||
image: {
|
||||
bg: "from-green-500/20 to-green-600/20",
|
||||
hover: "from-green-500/20 to-green-600/20",
|
||||
text: "text-green-400",
|
||||
},
|
||||
description: [
|
||||
"使用Next.js、Stripe支付和TailwindCSS构建的可扩展电子商务平台。",
|
||||
"采用移动优先的响应式设计,提供最佳用户体验。",
|
||||
"集成Stripe支付处理,确保交易安全。",
|
||||
"管理员仪表板,用于库存管理和订单处理。",
|
||||
],
|
||||
tech: ["Next.js", "Stripe", "TailwindCSS"],
|
||||
link: "#",
|
||||
...sharedCases[2],
|
||||
role: '全栈工程师',
|
||||
impact: '支撑上下游参与方的金融协作流程',
|
||||
systemType: '供应链金融平台',
|
||||
context: '连接企业、金融团队与合作方的协同业务系统。',
|
||||
title: '供应链金融平台',
|
||||
challenges: ['长链路流程状态与边界处理', '跨角色数据可见性策略', '审批与操作的高可追踪性'],
|
||||
responsibilities: ['实现核心流程模块与状态流转', '设计角色化视图与权限控制', '优化关键页面操作效率'],
|
||||
outcomes: ['提升参与方流程透明度', '降低金融业务交接摩擦', '支撑业务扩展中的稳定迭代'],
|
||||
description: ['面向多角色协同的金融业务流程系统。', '强调流程清晰、可靠与高效操作。'],
|
||||
tech: ['TypeScript', 'React', 'Node.js', 'PostgreSQL'],
|
||||
},
|
||||
{
|
||||
id: "portfolio",
|
||||
type: "experiment",
|
||||
status: "completed",
|
||||
role: "创作者",
|
||||
impact: "个人品牌和作品集展示",
|
||||
title: "个人作品集网站",
|
||||
tag: "portfolio",
|
||||
icon: "Globe",
|
||||
color: "purple",
|
||||
image: {
|
||||
bg: "from-purple-500/20 to-purple-600/20",
|
||||
hover: "from-purple-500/20 to-purple-600/20",
|
||||
text: "text-purple-400",
|
||||
},
|
||||
description: [
|
||||
"展示我的作品的个人作品集,使用HTML、TailwindCSS和Alpine.js构建。",
|
||||
"响应式设计,支持暗黑模式和平滑动画。",
|
||||
"通过最小化JavaScript和高效CSS优化性能。",
|
||||
"集成博客部分,支持markdown内容管理。",
|
||||
],
|
||||
tech: ["HTML", "TailwindCSS", "Alpine.js"],
|
||||
link: "#",
|
||||
...sharedCases[3],
|
||||
role: '前端工程师',
|
||||
impact: '整合园区管理核心业务模块',
|
||||
systemType: '工业智慧园区管理系统',
|
||||
context: '覆盖园区运营、资源和服务流程的数字化管理平台。',
|
||||
title: '工业智慧园区管理系统',
|
||||
challenges: ['多业务模块交互差异大', '运营看板信息层级复杂', '模块长期演进可维护性要求高'],
|
||||
responsibilities: ['开发关键模块并统一交互模式', '优化看板信息层级与可读性', '与后端及产品团队协同迭代交付'],
|
||||
outcomes: ['提升日常运营管理效率', '减少跨模块 UI 不一致问题', '支撑长期可维护迭代'],
|
||||
description: ['园区运营与服务协同的一体化系统。', '强调清晰性、一致性与可扩展演进。'],
|
||||
tech: ['React', 'TypeScript', 'ECharts', 'REST API'],
|
||||
},
|
||||
{
|
||||
...sharedCases[4],
|
||||
role: '创始人 & 开发者',
|
||||
impact: '在真实场景中探索 AI 协作开发工作流',
|
||||
systemType: 'AI 协作工作空间',
|
||||
context: '探索 AI 在软件工程中可落地协作方式的开放产品。',
|
||||
title: 'Elynd',
|
||||
challenges: ['在不增加流程负担前提下设计有效 AI 工作流'],
|
||||
responsibilities: ['产品探索与端到端实现'],
|
||||
outcomes: ['验证多种可实践的 AI 协作开发模式'],
|
||||
description: ['一个面向构建者的开放 AI 工作空间。', '作为研发探索方向,不作为职业主叙事。'],
|
||||
tech: ['TypeScript', 'React', 'AI 协作', '开源'],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
39
src/lib/data/uses.ts
Normal file
39
src/lib/data/uses.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import type { UsesGroup } from '@/types';
|
||||
|
||||
export const uses: UsesGroup[] = [
|
||||
{
|
||||
title: {
|
||||
en: 'Editor',
|
||||
zh: '编辑器',
|
||||
},
|
||||
items: ['VS Code', 'Cursor', 'Zed'],
|
||||
},
|
||||
{
|
||||
title: {
|
||||
en: 'Languages',
|
||||
zh: '语言',
|
||||
},
|
||||
items: ['TypeScript', 'JavaScript', 'SQL', 'Bash'],
|
||||
},
|
||||
{
|
||||
title: {
|
||||
en: 'Frameworks',
|
||||
zh: '框架',
|
||||
},
|
||||
items: ['React', 'Astro', 'Node.js', 'Vue 3'],
|
||||
},
|
||||
{
|
||||
title: {
|
||||
en: 'Infrastructure',
|
||||
zh: '基础设施',
|
||||
},
|
||||
items: ['Docker', 'Nginx', 'Linux', 'PostgreSQL'],
|
||||
},
|
||||
{
|
||||
title: {
|
||||
en: 'AI Workflow',
|
||||
zh: 'AI 协作工作流',
|
||||
},
|
||||
items: ['ChatGPT', 'Claude', 'Codex', 'Agentic development workflow'],
|
||||
},
|
||||
];
|
||||
@@ -1,220 +1,70 @@
|
||||
---
|
||||
import Layout from "@/layouts/Layout.astro";
|
||||
import GlassHeader from "@/components/GlassHeader";
|
||||
import Footer from "@/components/Footer";
|
||||
import Container from "@/components/ui/Container.astro";
|
||||
import HighlightBox from "@/components/markdown/HighlightBox.astro";
|
||||
import { useTranslations } from "@/i18n/utils";
|
||||
import type { Lang } from "@/types/i18n";
|
||||
import { defaultLang } from "@/i18n/ui";
|
||||
import {
|
||||
Sparkles,
|
||||
Heart,
|
||||
Zap,
|
||||
Mail,
|
||||
MessageSquare,
|
||||
Send,
|
||||
Github,
|
||||
Twitter,
|
||||
Linkedin,
|
||||
Globe
|
||||
} from "lucide-react";
|
||||
|
||||
const IconMap: Record<string, any> = {
|
||||
mail: Mail,
|
||||
wechat: MessageSquare,
|
||||
qq: MessageSquare, // Lucide doesn't have QQ, using MessageSquare
|
||||
send: Send,
|
||||
github: Github,
|
||||
twitter: Twitter,
|
||||
linkedin: Linkedin,
|
||||
globe: Globe,
|
||||
};
|
||||
import Layout from '@/layouts/Layout.astro';
|
||||
import GlassHeader from '@/components/GlassHeader';
|
||||
import Footer from '@/components/Footer';
|
||||
import Container from '@/components/ui/Container.astro';
|
||||
import { personalInfo } from '@/lib/data';
|
||||
import type { Lang } from '@/types/i18n';
|
||||
import { defaultLang } from '@/i18n/ui';
|
||||
|
||||
const lang = (Astro.currentLocale as Lang) || defaultLang;
|
||||
const t = useTranslations(lang);
|
||||
const pageTitle = t("about.title");
|
||||
const isZh = lang === 'zh';
|
||||
|
||||
const focusAreas = isZh
|
||||
? ['前端架构设计与模块治理', '大型企业应用工程化交付', '金融与区块链系统前端建设', 'AI 协作开发流程实践']
|
||||
: ['Frontend architecture and module governance', 'Large-scale enterprise application delivery', 'Financial and blockchain system engineering', 'AI-assisted development workflow practices'];
|
||||
|
||||
const experienceNotes = isZh
|
||||
? [
|
||||
'8 年专业开发经验,持续参与复杂业务系统建设。',
|
||||
'参与多个政府与金融科技相关系统,包括交易平台、区块链基础设施、产业管理系统。',
|
||||
'擅长跨角色协作与远程异步沟通,注重稳定交付与长期可维护性。',
|
||||
]
|
||||
: [
|
||||
'8 years of professional development experience across complex business systems.',
|
||||
'Contributed to government- and fintech-related systems, including trading platforms, blockchain infrastructure, and industrial management systems.',
|
||||
'Strong in cross-functional collaboration and remote async execution with long-term maintainability in mind.',
|
||||
];
|
||||
---
|
||||
|
||||
<Layout title={pageTitle}>
|
||||
<GlassHeader client:load transition:persist="header" lang={lang} />
|
||||
|
||||
<main class="min-h-screen relative overflow-hidden pt-16 sm:pt-20">
|
||||
<!-- Background Decor -->
|
||||
<div class="fixed inset-0 -z-10 h-full w-full bg-background">
|
||||
<div class="absolute inset-0 bg-gradient-to-br from-primary/5 via-transparent to-primary/5 dark:from-primary/10 dark:via-transparent dark:to-primary/5"></div>
|
||||
</div>
|
||||
<Layout title={isZh ? '关于' : 'About'}>
|
||||
<GlassHeader lang={lang} client:load transition:persist="header" />
|
||||
|
||||
<!-- Hero Section -->
|
||||
<section class="relative z-10 py-16 md:py-24">
|
||||
<Container>
|
||||
<div class="max-w-4xl mx-auto">
|
||||
<div class="flex flex-col md:flex-row items-center gap-12 mb-16">
|
||||
<div class="relative group">
|
||||
<div class="absolute -inset-1 rounded-full bg-gradient-to-r from-primary to-purple-600 opacity-75 blur transition duration-1000 group-hover:opacity-100 group-hover:duration-200"></div>
|
||||
<div class="relative h-48 w-48 rounded-full border-4 border-background overflow-hidden bg-muted">
|
||||
<img
|
||||
src="/avatar.png"
|
||||
alt="Joey Zhao"
|
||||
class="h-full w-full object-cover transition-transform duration-500 group-hover:scale-110"
|
||||
onerror="this.src='https://ui-avatars.com/api/?name=Joy+Zhao&background=0D8ABC&color=fff&size=200'"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="text-center md:text-left space-y-6">
|
||||
<h1 class="text-4xl md:text-6xl font-bold tracking-tight">
|
||||
<span class="bg-gradient-to-r from-foreground to-foreground/70 bg-clip-text text-transparent">
|
||||
{t('about.title')}
|
||||
</span>
|
||||
</h1>
|
||||
<p class="text-xl text-muted-foreground leading-relaxed">
|
||||
{t('about.description')}
|
||||
</p>
|
||||
</div>
|
||||
<main class="min-h-screen pt-24 pb-20">
|
||||
<Container>
|
||||
<section class="page-content-main">
|
||||
<h1 class="text-4xl font-bold tracking-tight sm:text-5xl">{isZh ? '关于我' : 'About'}</h1>
|
||||
<p class="mt-6 text-lg leading-relaxed text-muted-foreground">{personalInfo.description[lang]}</p>
|
||||
</section>
|
||||
|
||||
<section class="page-content-main mt-12 grid gap-6 lg:grid-cols-2">
|
||||
<article class="page-surface p-8">
|
||||
<h2 class="text-2xl font-bold tracking-tight">{isZh ? '背景' : 'Background'}</h2>
|
||||
<div class="mt-4 space-y-4 text-muted-foreground">
|
||||
{personalInfo.about[lang].map((line) => <p>{line}</p>)}
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<div class="grid gap-8">
|
||||
<!-- Intro Box -->
|
||||
<HighlightBox type="tip" title={t('about.intro.title')}>
|
||||
<div class="space-y-4 py-2">
|
||||
<p class="text-lg leading-relaxed whitespace-pre-line">{t('about.intro.content')}</p>
|
||||
<div class="p-4 rounded-xl bg-primary/5 border border-primary/10 italic text-primary/80">
|
||||
{t('about.intro.belief')}
|
||||
</div>
|
||||
</div>
|
||||
</HighlightBox>
|
||||
<article class="page-surface p-8">
|
||||
<h2 class="text-2xl font-bold tracking-tight">{isZh ? '技术焦点' : 'Technical Focus'}</h2>
|
||||
<ul class="mt-4 space-y-3 text-muted-foreground">
|
||||
{focusAreas.map((item) => (
|
||||
<li class="flex gap-3"><span class="mt-2 h-1.5 w-1.5 rounded-full bg-primary"></span><span>{item}</span></li>
|
||||
))}
|
||||
</ul>
|
||||
</article>
|
||||
</section>
|
||||
|
||||
<!-- Self Intro -->
|
||||
<div class="page-surface p-8 rounded-2xl border bg-card/40 backdrop-blur-md space-y-6">
|
||||
<h2 class="text-2xl font-bold flex items-center gap-3">
|
||||
<Sparkles className="w-6 h-6 text-primary" />
|
||||
{t('about.me.title')}
|
||||
</h2>
|
||||
<div class="prose dark:prose-invert max-w-none">
|
||||
<p class="text-lg leading-relaxed text-muted-foreground whitespace-pre-line">
|
||||
{t('about.me.content')}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Skills Grid -->
|
||||
<div class="grid md:grid-cols-2 gap-8">
|
||||
<div class="page-surface p-8 rounded-2xl border bg-card/40 backdrop-blur-md space-y-6">
|
||||
<h2 class="text-2xl font-bold flex items-center gap-3">
|
||||
<Zap className="w-6 h-6 text-yellow-500" />
|
||||
{t('about.skills.mastered.title')}
|
||||
</h2>
|
||||
<div class="flex flex-wrap gap-2">
|
||||
{(t('about.skills.mastered.items') as unknown as string[]).map((item: string) => (
|
||||
<div class="px-3 py-1.5 rounded-lg bg-primary/5 border border-primary/10 text-muted-foreground text-xs font-medium">
|
||||
{item}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="page-surface p-8 rounded-2xl border bg-card/40 backdrop-blur-md space-y-6">
|
||||
<h2 class="text-2xl font-bold flex items-center gap-3">
|
||||
<Heart className="w-6 h-6 text-red-500" />
|
||||
{t('about.skills.learning.title')}
|
||||
</h2>
|
||||
<div class="flex flex-wrap gap-2">
|
||||
{(t('about.skills.learning.items') as unknown as string[]).map((item: string) => (
|
||||
<div class="px-3 py-1.5 rounded-lg bg-muted/30 border border-border/50 text-muted-foreground/70 text-xs font-medium">
|
||||
{item}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Interests -->
|
||||
<div class="space-y-8">
|
||||
<h2 class="text-2xl font-bold flex items-center gap-3 px-2">
|
||||
<Heart className="w-6 h-6 text-red-500" />
|
||||
{t('about.interests.title')}
|
||||
</h2>
|
||||
<ul class="grid sm:grid-cols-2 gap-4">
|
||||
{(t('about.interests.items') as unknown as any[]).map((item) => (
|
||||
<li class="flex items-start gap-4 p-4 rounded-xl border border-border/40 bg-card/20 hover:bg-card/40 transition-colors group">
|
||||
<div class="mt-1.5 w-1.5 h-1.5 rounded-full bg-primary/40 group-hover:bg-primary transition-colors shrink-0" />
|
||||
<div class="space-y-1">
|
||||
<span class="font-bold text-foreground">{item.title}</span>
|
||||
<p class="text-sm text-muted-foreground leading-relaxed">{item.content}</p>
|
||||
</div>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- Socials & Community -->
|
||||
<div class="space-y-6">
|
||||
<h2 class="text-2xl font-bold flex items-center gap-3 px-2">
|
||||
<Globe className="w-6 h-6 text-primary" />
|
||||
{t('about.socials.title')}
|
||||
</h2>
|
||||
<div class="flex flex-wrap gap-4 px-2">
|
||||
{(t('about.socials.items') as unknown as any[]).map((item) => {
|
||||
const Icon = IconMap[item.icon] || Globe;
|
||||
return (
|
||||
<a
|
||||
href={item.link}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="flex items-center gap-2 px-4 py-2 rounded-lg bg-muted/30 border border-border/50 hover:border-primary/50 hover:bg-primary/5 transition-all group text-sm font-medium"
|
||||
>
|
||||
<Icon className="w-4 h-4 text-muted-foreground group-hover:text-primary transition-colors" />
|
||||
<span>{item.label}</span>
|
||||
</a>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Contact -->
|
||||
<div class="space-y-8 py-8">
|
||||
<div class="px-2 space-y-4">
|
||||
<h2 class="text-2xl font-bold flex items-center gap-3">
|
||||
<Mail className="w-6 h-6 text-primary" />
|
||||
{t('about.contact.title')}
|
||||
</h2>
|
||||
<p class="text-muted-foreground leading-relaxed max-w-2xl">
|
||||
{t('about.contact.warning')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||
{(t('about.contact.methods') as unknown as any[]).map((method) => {
|
||||
const Icon = IconMap[method.icon] || MessageSquare;
|
||||
const content = (
|
||||
<div class="flex items-center gap-4 p-4 rounded-xl border border-border/40 bg-card/20 hover:bg-card/40 transition-all group">
|
||||
<div class="p-2.5 rounded-lg bg-primary/5 text-primary/70 group-hover:bg-primary/10 group-hover:text-primary transition-colors">
|
||||
<Icon className="w-5 h-5" />
|
||||
</div>
|
||||
<div class="flex flex-col">
|
||||
<span class="text-[10px] font-bold text-muted-foreground uppercase tracking-widest">{method.label}</span>
|
||||
<span class="text-sm font-medium break-all">{method.value}</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
if (method.link) {
|
||||
return (
|
||||
<a href={method.link} class="block transition-transform hover:-translate-y-0.5">
|
||||
{content}
|
||||
</a>
|
||||
);
|
||||
}
|
||||
return <div class="cursor-default">{content}</div>;
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Container>
|
||||
</section>
|
||||
<section class="page-content-main mt-6 page-surface p-8">
|
||||
<h2 class="text-2xl font-bold tracking-tight">{isZh ? '经验概览' : 'Experience'}</h2>
|
||||
<ul class="mt-4 space-y-3 text-muted-foreground">
|
||||
{experienceNotes.map((item) => (
|
||||
<li class="flex gap-3"><span class="mt-2 h-1.5 w-1.5 rounded-full bg-primary"></span><span>{item}</span></li>
|
||||
))}
|
||||
</ul>
|
||||
</section>
|
||||
</Container>
|
||||
</main>
|
||||
|
||||
<Footer client:load lang={lang} />
|
||||
|
||||
<Footer lang={lang} client:load />
|
||||
</Layout>
|
||||
|
||||
@@ -43,16 +43,16 @@ const sortedBlogPosts = sortPostsByDate(blogPosts);
|
||||
|
||||
---
|
||||
|
||||
<BlogLayout title="Blog - Joey Zhao" description="Thoughts on AI products, full-stack development, and building in public. Explore my latest posts below.">
|
||||
<BlogLayout title="Writing - Joey Zhao" description="Engineering notes on architecture, delivery, and AI-assisted development workflows.">
|
||||
<main class="min-h-screen">
|
||||
<!-- Header Section -->
|
||||
<Container className="pt-24 pb-12">
|
||||
<div class="text-center mb-16">
|
||||
<h1 class="text-5xl md:text-6xl font-bold bg-gradient-to-r from-foreground via-primary to-primary dark:from-foreground dark:via-blue-200 dark:to-orange-300 bg-clip-text text-transparent mb-6">
|
||||
<span class="text-primary">Blog</span>
|
||||
<span class="text-primary">Writing</span>
|
||||
</h1>
|
||||
<p class="text-xl text-muted-foreground page-content-main">
|
||||
Thoughts on AI products, full-stack development, and building in public. Exploring the intersection of technology and product building.
|
||||
Engineering notes on architecture, delivery, and AI-assisted development workflows.
|
||||
</p>
|
||||
</div>
|
||||
</Container>
|
||||
|
||||
62
src/pages/contact.astro
Normal file
62
src/pages/contact.astro
Normal file
@@ -0,0 +1,62 @@
|
||||
---
|
||||
import Layout from '@/layouts/Layout.astro';
|
||||
import GlassHeader from '@/components/GlassHeader';
|
||||
import Footer from '@/components/Footer';
|
||||
import Container from '@/components/ui/Container.astro';
|
||||
import { contactIntents, contactMethods } from '@/lib/data';
|
||||
import type { Lang } from '@/types/i18n';
|
||||
import { defaultLang } from '@/i18n/ui';
|
||||
|
||||
const lang = (Astro.currentLocale as Lang) || defaultLang;
|
||||
const isZh = lang === 'zh';
|
||||
---
|
||||
|
||||
<Layout title={isZh ? '联系' : 'Contact'}>
|
||||
<GlassHeader lang={lang} client:load transition:persist="header" />
|
||||
|
||||
<main class="min-h-screen pt-24 pb-20">
|
||||
<Container>
|
||||
<section class="page-content-main">
|
||||
<h1 class="text-4xl font-bold tracking-tight sm:text-5xl">{isZh ? '联系' : 'Contact'}</h1>
|
||||
<p class="mt-4 text-lg text-muted-foreground">
|
||||
{isZh
|
||||
? '欢迎联系我沟通远程岗位或项目合作。默认优先响应远程岗位机会。'
|
||||
: 'Open to remote role opportunities and project collaboration. Remote role discussions are prioritized.'}
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section class="page-content-main mt-10 grid gap-6 md:grid-cols-2">
|
||||
{contactIntents.map((intent) => (
|
||||
<article id={intent.id} class="page-surface p-6">
|
||||
<h2 class="text-xl font-bold">{intent.title[lang]}</h2>
|
||||
<p class="mt-3 text-muted-foreground">{intent.description[lang]}</p>
|
||||
</article>
|
||||
))}
|
||||
</section>
|
||||
|
||||
<section class="page-content-main mt-6 page-surface p-6">
|
||||
<h2 class="text-xl font-bold">{isZh ? '联系方式' : 'Contact Methods'}</h2>
|
||||
<ul class="mt-4 space-y-3 text-sm">
|
||||
{contactMethods.map((method) => (
|
||||
<li class="flex flex-col gap-1 sm:flex-row sm:items-center sm:justify-between border-b border-border/70 pb-3">
|
||||
<span class="font-semibold text-foreground">{method.label[lang]}</span>
|
||||
{method.href ? (
|
||||
<a href={method.href} target="_blank" rel="noopener noreferrer" class="text-primary hover:text-primary/80 break-all">{method.value}</a>
|
||||
) : (
|
||||
<span class="text-muted-foreground break-all">{method.value}</span>
|
||||
)}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
|
||||
<p class="mt-5 text-xs text-muted-foreground">
|
||||
{isZh
|
||||
? '联系建议:请在邮件中注明岗位/项目背景、期望合作方式与时间窗口。'
|
||||
: 'Suggestion: include role/project context, expected collaboration model, and timeline in your first message.'}
|
||||
</p>
|
||||
</section>
|
||||
</Container>
|
||||
</main>
|
||||
|
||||
<Footer lang={lang} client:load />
|
||||
</Layout>
|
||||
@@ -1,375 +1,3 @@
|
||||
---
|
||||
import Layout from "@/layouts/Layout.astro";
|
||||
import GlassHeader from "@/components/GlassHeader";
|
||||
import Footer from "@/components/Footer";
|
||||
import Container from "@/components/ui/Container.astro";
|
||||
import { useTranslations } from "@/i18n/utils";
|
||||
import { Users, Wrench, Lightbulb, Handshake, CircleDollarSign, Clock, Target, Check, Calendar, Mail, Radio, Zap, Globe, Smartphone, ShoppingCart, Layout as LayoutIcon, ClipboardList } from "lucide-react";
|
||||
import type { Lang } from "@/types/i18n";
|
||||
import { defaultLang } from "@/i18n/ui";
|
||||
|
||||
const lang = Astro.currentLocale as Lang || defaultLang;
|
||||
const t = useTranslations(lang);
|
||||
const pageTitle = lang === 'zh' ? '合作' : 'Hire Me';
|
||||
return Astro.redirect('/contact', 301);
|
||||
---
|
||||
|
||||
<Layout title={pageTitle}>
|
||||
<GlassHeader lang={lang} client:load transition:persist="header" />
|
||||
<main class="min-h-screen">
|
||||
<!-- Hire Page Hero -->
|
||||
<section class="py-24 relative overflow-hidden">
|
||||
<div class="page-hero-overlay"></div>
|
||||
|
||||
<Container className="relative z-10">
|
||||
<div class="text-center mb-16">
|
||||
<span class="inline-block px-4 py-1 bg-primary/20 text-primary dark:text-primary rounded-full text-sm font-medium mb-4">
|
||||
{lang === 'zh' ? '专业合作' : 'Professional Collaboration'}
|
||||
</span>
|
||||
<h1 class="page-title-gradient text-5xl md:text-6xl font-bold mb-6">
|
||||
{lang === 'zh' ? '合作' : 'Hire Me'}
|
||||
</h1>
|
||||
<p class="text-lg text-muted-foreground page-content-narrow">
|
||||
{lang === 'zh'
|
||||
? '构建优秀产品,从找到合适的技术合伙人开始'
|
||||
: 'Building great products starts with finding the right technical partner'}
|
||||
</p>
|
||||
</div>
|
||||
</Container>
|
||||
</section>
|
||||
|
||||
<!-- Who I Work With -->
|
||||
<section class="py-16 relative">
|
||||
<Container>
|
||||
<div class="page-content-main">
|
||||
<div class="flex items-center gap-4 mb-8">
|
||||
<div class="w-12 h-12 bg-primary/20 rounded-xl flex items-center justify-center">
|
||||
<Users className="w-6 h-6 text-primary" />
|
||||
</div>
|
||||
<h2 class="text-3xl font-bold">
|
||||
{lang === 'zh' ? '我与谁合作' : 'Who I Work With'}
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<div class="grid md:grid-cols-2 gap-6">
|
||||
<div class="page-surface p-6">
|
||||
<h3 class="text-lg font-bold mb-3">
|
||||
{lang === 'zh' ? '初创公司' : 'Startups'}
|
||||
</h3>
|
||||
<p class="text-muted-foreground">
|
||||
{lang === 'zh'
|
||||
? '从零到一构建产品的早期创业团队,需要快速迭代和灵活响应的技术方案。'
|
||||
: 'Early-stage startup teams building products from scratch, needing fast iteration and flexible technical solutions.'}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="page-surface p-6">
|
||||
<h3 class="text-lg font-bold mb-3">
|
||||
{lang === 'zh' ? '独立创始人' : 'Indie Hackers'}
|
||||
</h3>
|
||||
<p class="text-muted-foreground">
|
||||
{lang === 'zh'
|
||||
? '独自构建产品的创业者,需要技术合伙人来实现产品愿景。'
|
||||
: 'Solo founders building products who need a technical partner to realize their product vision.'}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="page-surface p-6">
|
||||
<h3 class="text-lg font-bold mb-3">
|
||||
{lang === 'zh' ? '传统企业' : 'Traditional Businesses'}
|
||||
</h3>
|
||||
<p class="text-muted-foreground">
|
||||
{lang === 'zh'
|
||||
? '希望数字化转型的传统企业,需要现代化的技术解决方案。'
|
||||
: 'Traditional businesses looking to go digital and need modern technical solutions.'}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="page-surface p-6">
|
||||
<h3 class="text-lg font-bold mb-3">
|
||||
{lang === 'zh' ? '技术团队' : 'Technical Teams'}
|
||||
</h3>
|
||||
<p class="text-muted-foreground">
|
||||
{lang === 'zh'
|
||||
? '需要额外技术资源或特定技能集来推进项目的开发团队。'
|
||||
: 'Development teams needing additional technical resources or specific skill sets to move projects forward.'}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Container>
|
||||
</section>
|
||||
|
||||
<!-- What I Build -->
|
||||
<section class="py-16 relative page-section-soft">
|
||||
<Container>
|
||||
<div class="page-content-main">
|
||||
<div class="flex items-center gap-4 mb-8">
|
||||
<div class="w-12 h-12 bg-blue-500/20 rounded-xl flex items-center justify-center">
|
||||
<Wrench className="w-6 h-6 text-blue-500" />
|
||||
</div>
|
||||
<h2 class="text-3xl font-bold">
|
||||
{lang === 'zh' ? '我能构建什么' : 'What I Build'}
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<div class="space-y-6">
|
||||
<div class="page-surface p-8">
|
||||
<div class="flex items-start gap-4">
|
||||
<div class="w-12 h-12 bg-gradient-to-br from-primary to-blue-600 rounded-xl flex items-center justify-center flex-shrink-0">
|
||||
<Zap className="w-6 h-6 text-white" />
|
||||
</div>
|
||||
<div>
|
||||
<h3 class="text-xl font-bold mb-2">AI 产品与应用</h3>
|
||||
<p class="text-muted-foreground">
|
||||
{lang === 'zh'
|
||||
? '从 AI 助手到 Agent 系统,从 RAG 应用到 AI 驱动的 SaaS 产品。我可以帮助你将 AI 能力产品化。'
|
||||
: 'From AI assistants to Agent systems, from RAG applications to AI-powered SaaS products. I can help you productize AI capabilities.'}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="page-surface p-8">
|
||||
<div class="flex items-start gap-4">
|
||||
<div class="w-12 h-12 bg-gradient-to-br from-green-600 to-teal-600 rounded-xl flex items-center justify-center flex-shrink-0">
|
||||
<Globe className="w-6 h-6 text-white" />
|
||||
</div>
|
||||
<div>
|
||||
<h3 class="text-xl font-bold mb-2">Web 应用</h3>
|
||||
<p class="text-muted-foreground">
|
||||
{lang === 'zh'
|
||||
? '现代 Web 应用、SaaS 平台、电商系统、内容管理系统等。使用 React、Next.js、Node.js 等技术栈。'
|
||||
: 'Modern web applications, SaaS platforms, e-commerce systems, content management systems, etc. Using React, Next.js, Node.js and other tech stacks.'}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="page-surface p-8">
|
||||
<div class="flex items-start gap-4">
|
||||
<div class="w-12 h-12 bg-gradient-to-br from-orange-600 to-red-600 rounded-xl flex items-center justify-center flex-shrink-0">
|
||||
<Smartphone className="w-6 h-6 text-white" />
|
||||
</div>
|
||||
<div>
|
||||
<h3 class="text-xl font-bold mb-2">全栈产品</h3>
|
||||
<p class="text-muted-foreground">
|
||||
{lang === 'zh'
|
||||
? '从前端到后端,从数据库到部署,我提供完整的全栈开发能力,一个项目只需一个开发者。'
|
||||
: 'From frontend to backend, from database to deployment, I provide complete full-stack development capabilities - one developer for the entire project.'}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Container>
|
||||
</section>
|
||||
|
||||
<!-- Engagement Models -->
|
||||
<section class="py-16 relative">
|
||||
<Container>
|
||||
<div class="page-content-main">
|
||||
<div class="flex items-center gap-4 mb-8">
|
||||
<div class="w-12 h-12 bg-green-500/20 rounded-xl flex items-center justify-center">
|
||||
<ClipboardList className="w-6 h-6 text-green-500" />
|
||||
</div>
|
||||
<h2 class="text-3xl font-bold">
|
||||
{lang === 'zh' ? '合作模式' : 'Engagement Models'}
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<div class="grid md:grid-cols-3 gap-6">
|
||||
<div class="page-surface p-6 text-center">
|
||||
<div class="w-12 h-12 bg-green-500/20 rounded-xl flex items-center justify-center mx-auto mb-4">
|
||||
<Handshake className="w-6 h-6 text-green-600 dark:text-green-400" />
|
||||
</div>
|
||||
<h3 class="text-lg font-bold mb-2">
|
||||
{lang === 'zh' ? '技术合伙人' : 'Technical Co-founder'}
|
||||
</h3>
|
||||
<p class="text-sm text-muted-foreground">
|
||||
{lang === 'zh'
|
||||
? '长期合作,股权合作,共同承担创业风险与回报'
|
||||
: 'Long-term partnership, equity-based, sharing startup risks and rewards'}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="page-surface p-6 text-center">
|
||||
<div class="w-12 h-12 bg-blue-500/20 rounded-xl flex items-center justify-center mx-auto mb-4">
|
||||
<CircleDollarSign className="w-6 h-6 text-blue-600 dark:text-blue-400" />
|
||||
</div>
|
||||
<h3 class="text-lg font-bold mb-2">
|
||||
{lang === 'zh' ? '项目制' : 'Project-based'}
|
||||
</h3>
|
||||
<p class="text-sm text-muted-foreground">
|
||||
{lang === 'zh'
|
||||
? '按项目交付,固定价格或按阶段付款,适合明确需求的项目'
|
||||
: 'Fixed-price or milestone-based, suitable for projects with clear requirements'}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="page-surface p-6 text-center">
|
||||
<div class="w-12 h-12 bg-primary/20 rounded-xl flex items-center justify-center mx-auto mb-4">
|
||||
<Clock className="w-6 h-6 text-primary" />
|
||||
</div>
|
||||
<h3 class="text-lg font-bold mb-2">
|
||||
{lang === 'zh' ? '咨询/顾问' : 'Consulting'}
|
||||
</h3>
|
||||
<p class="text-sm text-muted-foreground">
|
||||
{lang === 'zh'
|
||||
? '按小时咨询,帮助你做出技术决策,审查代码,指导团队'
|
||||
: 'Hourly consulting, helping you make technical decisions, code reviews, team guidance'}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Container>
|
||||
</section>
|
||||
|
||||
<!-- Delivery Style -->
|
||||
<section class="py-16 relative page-section-soft">
|
||||
<Container>
|
||||
<div class="page-content-main">
|
||||
<div class="flex items-center gap-4 mb-8">
|
||||
<div class="w-12 h-12 bg-yellow-500/20 rounded-xl flex items-center justify-center">
|
||||
<Target className="w-6 h-6 text-yellow-600 dark:text-yellow-400" />
|
||||
</div>
|
||||
<h2 class="text-3xl font-bold">
|
||||
{lang === 'zh' ? '工作方式' : 'Delivery Style'}
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<div class="page-surface p-8">
|
||||
<ul class="space-y-4">
|
||||
<li class="flex items-start gap-3">
|
||||
<Check className="w-6 h-6 text-green-500 shrink-0 mt-0.5" />
|
||||
<div>
|
||||
<strong class="block mb-1">
|
||||
{lang === 'zh' ? '敏捷迭代' : 'Agile Iteration'}
|
||||
</strong>
|
||||
<p class="text-muted-foreground text-sm">
|
||||
{lang === 'zh'
|
||||
? '2周一个迭代,快速交付可用功能,持续获取反馈'
|
||||
: '2-week sprints, rapid delivery of usable features, continuous feedback'}
|
||||
</p>
|
||||
</div>
|
||||
</li>
|
||||
<li class="flex items-start gap-3">
|
||||
<Check className="w-6 h-6 text-green-500 shrink-0 mt-0.5" />
|
||||
<div>
|
||||
<strong class="block mb-1">
|
||||
{lang === 'zh' ? '透明沟通' : 'Transparent Communication'}
|
||||
</strong>
|
||||
<p class="text-muted-foreground text-sm">
|
||||
{lang === 'zh'
|
||||
? '定期同步进度,及时报告问题,保持信息对称'
|
||||
: 'Regular progress updates, timely issue reporting, keeping information symmetric'}
|
||||
</p>
|
||||
</div>
|
||||
</li>
|
||||
<li class="flex items-start gap-3">
|
||||
<Check className="w-6 h-6 text-green-500 shrink-0 mt-0.5" />
|
||||
<div>
|
||||
<strong class="block mb-1">
|
||||
{lang === 'zh' ? '代码质量' : 'Code Quality'}
|
||||
</strong>
|
||||
<p class="text-muted-foreground text-sm">
|
||||
{lang === 'zh'
|
||||
? '完整的测试覆盖,清晰的文档注释,可维护的代码结构'
|
||||
: 'Complete test coverage, clear documentation, maintainable code structure'}
|
||||
</p>
|
||||
</div>
|
||||
</li>
|
||||
<li class="flex items-start gap-3">
|
||||
<Check className="w-6 h-6 text-green-500 shrink-0 mt-0.5" />
|
||||
<div>
|
||||
<strong class="block mb-1">
|
||||
{lang === 'zh' ? '产品思维' : 'Product Thinking'}
|
||||
</strong>
|
||||
<p class="text-muted-foreground text-sm">
|
||||
{lang === 'zh'
|
||||
? '不只是写代码,还思考产品价值,用户体验 and 业务目标'
|
||||
: 'Not just writing code, but also thinking about product value, user experience, and business goals'}
|
||||
</p>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</Container>
|
||||
</section>
|
||||
|
||||
<!-- Availability -->
|
||||
<section class="py-16 relative">
|
||||
<Container>
|
||||
<div class="page-content-main">
|
||||
<div class="flex items-center gap-4 mb-8">
|
||||
<div class="w-12 h-12 bg-red-500/20 rounded-xl flex items-center justify-center">
|
||||
<Calendar className="w-6 h-6 text-red-500" />
|
||||
</div>
|
||||
<h2 class="text-3xl font-bold">
|
||||
{lang === 'zh' ? '当前可用性' : 'Availability'}
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<div class="page-surface p-8">
|
||||
<div class="flex items-center gap-4 mb-6">
|
||||
<div class="w-4 h-4 bg-green-500 rounded-full animate-pulse"></div>
|
||||
<span class="text-lg font-semibold">
|
||||
{lang === 'zh' ? '目前可接受新项目' : 'Currently Available for New Projects'}
|
||||
</span>
|
||||
</div>
|
||||
<p class="text-muted-foreground mb-6">
|
||||
{lang === 'zh'
|
||||
? '我目前可以接受 1-2 个新项目。偏好长期合作模式(技术合伙人或长期项目),但也会考虑短期项目。'
|
||||
: 'I can currently take on 1-2 new projects. Prefer long-term partnership (technical co-founder or long-term projects), but also open to short-term projects.'}
|
||||
</p>
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<span class="px-3 py-1 bg-green-500/20 text-green-600 dark:text-green-400 rounded-full text-sm">
|
||||
{lang === 'zh' ? '可立即开始' : 'Available immediately'}
|
||||
</span>
|
||||
<span class="px-3 py-1 bg-blue-500/20 text-blue-600 dark:text-blue-400 rounded-full text-sm">
|
||||
{lang === 'zh' ? '远程工作' : 'Remote only'}
|
||||
</span>
|
||||
<span class="px-3 py-1 bg-primary/20 text-primary dark:text-primary rounded-full text-sm">
|
||||
{lang === 'zh' ? '中英双语' : 'Bilingual EN/ZH'}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Container>
|
||||
</section>
|
||||
|
||||
<!-- Contact CTA -->
|
||||
<section class="py-16 relative page-section-soft">
|
||||
<Container>
|
||||
<div class="page-content-main text-center">
|
||||
<h2 class="text-3xl font-bold mb-6">
|
||||
{lang === 'zh' ? '准备好一起构建了吗?' : 'Ready to build together?'}
|
||||
</h2>
|
||||
<p class="text-lg text-muted-foreground mb-8 page-content-narrow">
|
||||
{lang === 'zh'
|
||||
? '告诉我你的项目想法,让我们看看能否合作。'
|
||||
: "Tell me about your project idea and let's see if we can work together."}
|
||||
</p>
|
||||
<div class="flex flex-col sm:flex-row gap-4 justify-center">
|
||||
<a
|
||||
href="mailto:zhaoguiyang18@gmail.com"
|
||||
class="bg-primary hover:bg-primary/90 text-white px-8 py-3 rounded-lg font-semibold transition-colors inline-flex items-center justify-center gap-2"
|
||||
>
|
||||
<Mail className="w-5 h-5" />
|
||||
{lang === 'zh' ? '发送邮件' : 'Send Email'}
|
||||
</a>
|
||||
<a
|
||||
href={lang === 'zh' ? '/zh/now' : '/now'}
|
||||
class="border border-primary text-primary hover:bg-primary hover:text-white px-8 py-3 rounded-lg font-semibold transition-colors inline-flex items-center justify-center gap-2"
|
||||
>
|
||||
<Radio className="w-5 h-5" />
|
||||
{lang === 'zh' ? '了解更多关于我' : 'Learn More About Me'}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</Container>
|
||||
</section>
|
||||
</main>
|
||||
<Footer lang={lang} client:only="react" />
|
||||
</Layout>
|
||||
|
||||
@@ -1,239 +1,109 @@
|
||||
---
|
||||
import Layout from "@/layouts/Layout.astro";
|
||||
import GlassHeader from "@/components/GlassHeader";
|
||||
import Footer from "@/components/Footer";
|
||||
import Container from "@/components/ui/Container.astro";
|
||||
import ProjectCard from "@/components/ProjectCard.astro";
|
||||
import { useTranslations } from "@/i18n/utils";
|
||||
import type { Lang } from "@/types/i18n";
|
||||
import { defaultLang } from "@/i18n/ui";
|
||||
import { personalInfo, services, projects } from "@/lib/data/index";
|
||||
import Layout from '@/layouts/Layout.astro';
|
||||
import GlassHeader from '@/components/GlassHeader';
|
||||
import Footer from '@/components/Footer';
|
||||
import Container from '@/components/ui/Container.astro';
|
||||
import { personalInfo, projects } from '@/lib/data';
|
||||
import type { Lang } from '@/types/i18n';
|
||||
import { defaultLang } from '@/i18n/ui';
|
||||
import { sortPostsByDate } from '@/utils/blog-utils';
|
||||
|
||||
const lang = (Astro.currentLocale as Lang) || defaultLang;
|
||||
const t = useTranslations(lang);
|
||||
const pageTitle = t("site.title");
|
||||
const prefix = lang === "zh" ? "/zh" : "";
|
||||
const isZh = lang === 'zh';
|
||||
const prefix = isZh ? '/zh' : '';
|
||||
const featuredProjects = projects[lang].filter((item) => item.featured).slice(0, 3);
|
||||
|
||||
const localizedServices = services[lang];
|
||||
const featuredProjects = projects[lang].filter((project) => project.featured);
|
||||
const proofItems = isZh
|
||||
? ['8 年 Web 开发经验', '企业级复杂系统交付', '金融与区块链基础设施', '远程协作与稳定交付']
|
||||
: ['8 years in web engineering', 'Enterprise-grade complex systems', 'Finance & blockchain infrastructure', 'Remote-first stable delivery'];
|
||||
|
||||
const careerMilestones = [
|
||||
{
|
||||
period: t("home.career.card1.period"),
|
||||
title: t("home.career.card1.title"),
|
||||
outcome: t("home.career.card1.outcome"),
|
||||
},
|
||||
{
|
||||
period: t("home.career.card2.period"),
|
||||
title: t("home.career.card2.title"),
|
||||
outcome: t("home.career.card2.outcome"),
|
||||
},
|
||||
{
|
||||
period: t("home.career.card3.period"),
|
||||
title: t("home.career.card3.title"),
|
||||
outcome: t("home.career.card3.outcome"),
|
||||
},
|
||||
];
|
||||
const headline = isZh
|
||||
? '资深前端工程师,构建复杂系统与 AI 协作产品'
|
||||
: 'Senior Frontend Engineer building complex systems and AI-assisted products';
|
||||
|
||||
const keyFacts = [
|
||||
t("home.trust.item1"),
|
||||
t("home.trust.item3"),
|
||||
t("home.trust.item4"),
|
||||
];
|
||||
const intro = personalInfo.description[lang];
|
||||
|
||||
const postModules = await import.meta.glob('./blog/posts/*.md', { eager: true });
|
||||
const latestPosts = sortPostsByDate(
|
||||
Object.values(postModules).map((post: any) => ({
|
||||
title: post.frontmatter.title,
|
||||
description: post.frontmatter.description || '',
|
||||
slug: post.url?.split('/').filter(Boolean).pop() || '',
|
||||
date: post.frontmatter.date || post.frontmatter.pubDate || '',
|
||||
})),
|
||||
).slice(0, 3);
|
||||
---
|
||||
|
||||
<Layout title={pageTitle}>
|
||||
<GlassHeader client:load transition:persist="header" lang={lang} />
|
||||
<Layout title={isZh ? '首页' : 'Home'}>
|
||||
<GlassHeader lang={lang} client:load transition:persist="header" />
|
||||
|
||||
<main class="min-h-screen">
|
||||
<div class="relative overflow-hidden">
|
||||
<div class="absolute inset-0 -z-10">
|
||||
<div class="absolute left-1/2 top-0 h-[600px] w-[800px] -translate-x-1/2 -translate-y-1/2 bg-primary/10 blur-[120px]"></div>
|
||||
<div class="absolute right-0 top-1/4 h-[400px] w-[400px] bg-purple-500/5 blur-[100px]"></div>
|
||||
<div class="absolute left-0 top-1/2 h-[400px] w-[400px] bg-orange-500/5 blur-[100px]"></div>
|
||||
</div>
|
||||
<section class="relative flex min-h-[80vh] items-center py-20 lg:py-28">
|
||||
<Container className="relative z-10">
|
||||
<div class="mx-auto max-w-5xl space-y-12">
|
||||
<div class="space-y-8 text-center">
|
||||
<div class="space-y-4">
|
||||
<h1 class="text-5xl font-bold tracking-tight sm:text-7xl lg:text-8xl">
|
||||
<span class="bg-gradient-to-b from-foreground to-foreground/70 bg-clip-text text-transparent">{personalInfo.name}</span>
|
||||
</h1>
|
||||
<p class="text-2xl font-medium text-foreground/80 sm:text-3xl">{personalInfo.position[lang]}</p>
|
||||
</div>
|
||||
|
||||
<p class="mx-auto max-w-2xl text-lg leading-relaxed text-muted-foreground/90 sm:text-xl">{t("home.hero.summary")}</p>
|
||||
<main class="min-h-screen pt-24 pb-20">
|
||||
<Container>
|
||||
<section class="page-content-main space-y-8">
|
||||
<p class="text-sm font-semibold uppercase tracking-[0.2em] text-primary">{isZh ? 'ENGINEERING PROFILE' : 'ENGINEERING PROFILE'}</p>
|
||||
<h1 class="text-4xl font-bold tracking-tight text-foreground sm:text-5xl lg:text-6xl">{headline}</h1>
|
||||
<p class="max-w-3xl text-lg leading-relaxed text-muted-foreground">{intro}</p>
|
||||
|
||||
<div class="flex flex-wrap items-center justify-center gap-4">
|
||||
<a href={`${prefix}/about`} class="inline-flex h-12 items-center justify-center rounded-full bg-primary px-8 text-sm font-semibold text-primary-foreground shadow-lg shadow-primary/20 transition-all hover:translate-y-[-2px] hover:bg-primary/90 hover:shadow-xl hover:shadow-primary/30">
|
||||
{t("home.hero.ctaPrimary")}
|
||||
</a>
|
||||
<a href={`${prefix}/hire`} class="inline-flex h-12 items-center justify-center rounded-full border border-border bg-background/50 px-8 text-sm font-semibold text-foreground backdrop-blur-sm transition-all hover:translate-y-[-2px] hover:bg-muted">
|
||||
{t("home.hero.ctaSecondary")}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mx-auto max-w-3xl space-y-8">
|
||||
<div class="flex flex-wrap items-center justify-center gap-3 text-sm">
|
||||
{keyFacts.map((item) => (
|
||||
<span class="inline-flex items-center rounded-full border border-primary/10 bg-primary/5 px-4 py-1.5 font-medium text-primary/80 backdrop-blur-sm">
|
||||
<span class="mr-2 h-1 w-1 rounded-full bg-primary/50"></span>
|
||||
{item}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
<div class="group relative">
|
||||
<div class="absolute -inset-1 rounded-2xl bg-gradient-to-r from-primary/20 to-purple-500/20 opacity-50 blur transition duration-1000 group-hover:opacity-100 group-hover:duration-200"></div>
|
||||
<div class="page-surface relative overflow-hidden rounded-2xl border bg-card/40 backdrop-blur-md">
|
||||
<div class="flex items-center justify-between border-b border-border/50 bg-muted/20 px-5 py-3">
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="h-3 w-3 rounded-full bg-red-400/80 shadow-[0_0_8px_rgba(248,113,113,0.4)]"></span>
|
||||
<span class="h-3 w-3 rounded-full bg-yellow-400/80 shadow-[0_0_8px_rgba(250,204,21,0.4)]"></span>
|
||||
<span class="h-3 w-3 rounded-full bg-green-400/80 shadow-[0_0_8px_rgba(74,222,128,0.4)]"></span>
|
||||
</div>
|
||||
<span class="font-mono text-xs font-medium text-muted-foreground/70 tracking-tight">{personalInfo.terminal.username}</span>
|
||||
</div>
|
||||
<div class="space-y-3 p-6 font-mono text-sm leading-relaxed text-foreground/90">
|
||||
<div class="flex gap-3">
|
||||
<span class="text-primary font-bold">~</span>
|
||||
<p><span class="text-primary/70">whoami</span> {personalInfo.name}</p>
|
||||
</div>
|
||||
<div class="flex gap-3">
|
||||
<span class="text-primary font-bold">~</span>
|
||||
<p><span class="text-primary/70">role</span> {personalInfo.position[lang]}</p>
|
||||
</div>
|
||||
<div class="flex gap-3">
|
||||
<span class="text-primary font-bold">~</span>
|
||||
<p><span class="text-primary/70">stack</span> TypeScript, React, Node.js, Astro</p>
|
||||
</div>
|
||||
<div class="flex gap-3">
|
||||
<span class="text-primary font-bold">~</span>
|
||||
<p><span class="text-primary/70">status</span> {t("home.hero.terminalStatus")}</p>
|
||||
</div>
|
||||
<div class="flex gap-3">
|
||||
<span class="text-primary font-bold">~</span>
|
||||
<p><span class="text-primary/70">contact</span> {personalInfo.github}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-wrap gap-4 pt-2">
|
||||
<a href={`${prefix}/contact`} class="inline-flex h-11 items-center rounded-lg bg-primary px-6 text-sm font-semibold text-primary-foreground hover:bg-primary/90">{isZh ? '远程岗位沟通' : 'Hire for Remote Role'}</a>
|
||||
<a href={`${prefix}/contact#project-collaboration`} class="inline-flex h-11 items-center rounded-lg border border-border px-6 text-sm font-semibold text-foreground hover:bg-muted">{isZh ? '发起项目合作' : 'Start a Project'}</a>
|
||||
</div>
|
||||
</Container>
|
||||
</section>
|
||||
|
||||
<div class="space-y-24 pb-24">
|
||||
<section class="relative">
|
||||
<div class="py-24 lg:py-32">
|
||||
<Container>
|
||||
<div class="mb-16 flex flex-col gap-6 md:flex-row md:items-end md:justify-between">
|
||||
<div class="space-y-4">
|
||||
<h2 class="text-4xl font-bold tracking-tight sm:text-5xl lg:text-6xl">{t("home.services.title")}</h2>
|
||||
<p class="max-w-2xl text-lg leading-relaxed text-muted-foreground/90">{t("home.services.description")}</p>
|
||||
</div>
|
||||
<a href={`${prefix}/services`} class="inline-flex items-center text-sm font-bold text-primary transition-colors hover:text-primary/70">
|
||||
{t("services.viewAll")}
|
||||
<svg class="ml-1 h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 8l4 4m0 0l-4 4m4-4H3"/></svg>
|
||||
</a>
|
||||
</div>
|
||||
<ul class="grid gap-3 sm:grid-cols-2 pt-4">
|
||||
{proofItems.map((item) => (
|
||||
<li class="page-surface p-4 text-sm font-medium text-foreground/90">{item}</li>
|
||||
))}
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<div class="grid gap-10 md:grid-cols-2">
|
||||
{localizedServices.map((service) => (
|
||||
<article class="group relative rounded-2xl p-6 transition-all duration-500 hover:bg-card/50 hover:shadow-xl hover:shadow-primary/5">
|
||||
<div class="mb-8 flex items-center gap-6">
|
||||
<div class={`flex h-14 w-14 items-center justify-center rounded-2xl bg-gradient-to-br ${service.icon.gradient} text-white shadow-lg transition-transform duration-500 group-hover:scale-110 group-hover:rotate-3`}>
|
||||
<svg class="h-6 w-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<Fragment set:html={service.icon.svg} />
|
||||
</svg>
|
||||
</div>
|
||||
<h3 class="text-2xl font-bold tracking-tight">{service.title}</h3>
|
||||
</div>
|
||||
<ul class="space-y-4">
|
||||
{service.items.slice(0, 3).map((item) => (
|
||||
<li class="flex items-start gap-3 text-muted-foreground group-hover:text-foreground/90 transition-colors">
|
||||
<span class="mt-2.5 h-1 w-1 shrink-0 rounded-full bg-primary/30 group-hover:bg-primary transition-all"></span>
|
||||
<span class="text-base leading-relaxed">{item}</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</article>
|
||||
))}
|
||||
</div>
|
||||
</Container>
|
||||
<section class="page-content-main mt-20">
|
||||
<div class="mb-8 flex items-end justify-between gap-4">
|
||||
<h2 class="text-2xl font-bold tracking-tight sm:text-3xl">{isZh ? '精选工程案例' : 'Selected Engineering Cases'}</h2>
|
||||
<a href={`${prefix}/projects`} class="text-sm font-semibold text-primary hover:text-primary/80">{isZh ? '查看全部案例' : 'View All Cases'}</a>
|
||||
</div>
|
||||
|
||||
<div class="grid gap-6 lg:grid-cols-3">
|
||||
{featuredProjects.map((project) => (
|
||||
<article class="page-surface p-6">
|
||||
<p class="text-xs font-semibold uppercase tracking-wider text-primary/80">{project.systemType}</p>
|
||||
<h3 class="mt-2 text-lg font-bold">{project.title}</h3>
|
||||
<p class="mt-3 text-sm leading-relaxed text-muted-foreground">{project.context}</p>
|
||||
<p class="mt-4 text-xs font-semibold uppercase tracking-wide text-foreground/70">{isZh ? '结果' : 'Outcome'}</p>
|
||||
<p class="mt-1 text-sm text-foreground/90">{project.outcomes?.[0] ?? project.impact}</p>
|
||||
</article>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="relative">
|
||||
<Container>
|
||||
<div class="mb-16 space-y-3">
|
||||
<h2 class="text-4xl font-bold tracking-tight sm:text-5xl">{t("home.career.title")}</h2>
|
||||
<p class="max-w-xl text-lg text-muted-foreground">{t("home.career.description")}</p>
|
||||
</div>
|
||||
<section class="page-content-main mt-20">
|
||||
<div class="mb-8 flex items-end justify-between gap-4">
|
||||
<h2 class="text-2xl font-bold tracking-tight sm:text-3xl">{isZh ? '最新写作' : 'Latest Writing'}</h2>
|
||||
<a href={`${prefix}/blog`} class="text-sm font-semibold text-primary hover:text-primary/80">{isZh ? '查看全部文章' : 'View All Writing'}</a>
|
||||
</div>
|
||||
|
||||
<div class="relative mx-auto max-w-4xl">
|
||||
<div class="absolute left-0 top-0 h-full w-px bg-gradient-to-b from-primary/50 via-primary/10 to-transparent sm:left-1/2"></div>
|
||||
|
||||
<div class="space-y-12">
|
||||
{careerMilestones.map((item, index) => (
|
||||
<div class={`relative flex flex-col sm:flex-row ${index % 2 === 0 ? 'sm:flex-row-reverse' : ''}`}>
|
||||
<div class="absolute -left-1.5 top-2 h-3 w-3 rounded-full border-2 border-primary bg-background sm:left-1/2 sm:-ml-1.5"></div>
|
||||
<div class="w-full pl-8 sm:w-1/2 sm:px-12">
|
||||
<article class="rounded-2xl p-4 transition-all hover:bg-card/40">
|
||||
<span class="text-xs font-bold uppercase tracking-wider text-primary/60">{item.period}</span>
|
||||
<h3 class="mt-2 text-xl font-bold leading-tight">{item.title}</h3>
|
||||
<p class="mt-3 text-sm leading-relaxed text-muted-foreground/90">{item.outcome}</p>
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-16 text-center">
|
||||
<a href={`${prefix}/about`} class="inline-flex h-12 items-center rounded-full border border-border bg-background px-8 text-sm font-semibold transition-all hover:bg-muted">
|
||||
{t("home.career.cta")}
|
||||
</a>
|
||||
</div>
|
||||
</Container>
|
||||
</section>
|
||||
|
||||
<section class="relative">
|
||||
<div class="py-24 lg:py-32">
|
||||
<Container>
|
||||
<div class="mb-12 space-y-3">
|
||||
<h2 class="text-4xl font-bold tracking-tight sm:text-5xl">{t("home.featured.title")}</h2>
|
||||
</div>
|
||||
|
||||
<div class="grid gap-6 md:grid-cols-2 lg:grid-cols-3">
|
||||
{featuredProjects.map((project) => (
|
||||
<ProjectCard project={project} lang={lang} />
|
||||
))}
|
||||
</div>
|
||||
</Container>
|
||||
<div class="grid gap-4 md:grid-cols-3">
|
||||
{latestPosts.map((post) => (
|
||||
<article class="page-surface p-5">
|
||||
<p class="text-xs font-semibold uppercase tracking-wider text-primary/80">{post.date}</p>
|
||||
<h3 class="mt-2 text-base font-bold leading-snug">
|
||||
<a href={`${prefix}/blog/posts/${post.slug}`} class="hover:text-primary">{post.title}</a>
|
||||
</h3>
|
||||
<p class="mt-2 line-clamp-3 text-sm text-muted-foreground">{post.description}</p>
|
||||
</article>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="relative">
|
||||
<Container>
|
||||
<div class="flex flex-col items-center text-center">
|
||||
<h2 class="text-4xl font-bold tracking-tight sm:text-5xl lg:text-6xl mb-6">{t("home.final.title")}</h2>
|
||||
<p class="text-lg leading-relaxed text-muted-foreground/90 sm:text-xl max-w-2xl mb-10">{t("home.final.description")}</p>
|
||||
<div class="flex flex-wrap justify-center gap-4">
|
||||
<a href={`${prefix}/about`} class="inline-flex h-12 items-center justify-center rounded-full bg-primary px-8 text-sm font-semibold text-primary-foreground transition-all hover:translate-y-[-2px] hover:bg-primary/90">
|
||||
{t("home.final.ctaPrimary")}
|
||||
</a>
|
||||
<a href={`${prefix}/hire`} class="inline-flex h-12 items-center justify-center rounded-full border border-border px-8 text-sm font-semibold text-foreground transition-all hover:translate-y-[-2px] hover:bg-muted">
|
||||
{t("home.final.ctaSecondary")}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</Container>
|
||||
<section class="page-content-main mt-20 page-surface p-8">
|
||||
<h2 class="text-2xl font-bold tracking-tight">{isZh ? '下一步' : 'Next Step'}</h2>
|
||||
<p class="mt-3 text-muted-foreground">{isZh ? '如果你正在招聘远程工程师,或希望推进复杂系统项目,我很乐意沟通。' : 'If you are hiring for a remote engineering role or need help shipping a complex system, I would love to connect.'}</p>
|
||||
<div class="mt-6 flex flex-wrap gap-3">
|
||||
<a href={`${prefix}/contact`} class="inline-flex h-10 items-center rounded-lg bg-primary px-5 text-sm font-semibold text-primary-foreground">{isZh ? '联系我' : 'Contact Me'}</a>
|
||||
<a href={`${prefix}/about`} class="inline-flex h-10 items-center rounded-lg border border-border px-5 text-sm font-semibold hover:bg-muted">{isZh ? '了解更多' : 'About Me'}</a>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</Container>
|
||||
</main>
|
||||
|
||||
<Footer lang={lang} client:only="react" />
|
||||
<Footer lang={lang} client:load />
|
||||
</Layout>
|
||||
|
||||
@@ -1,228 +1,67 @@
|
||||
---
|
||||
import Layout from "@/layouts/Layout.astro";
|
||||
import GlassHeader from "@/components/GlassHeader";
|
||||
import Footer from "@/components/Footer";
|
||||
import Container from "@/components/ui/Container.astro";
|
||||
import { useTranslations } from "@/i18n/utils";
|
||||
import { Hammer, Zap, Telescope, Bot, Target, Rocket, Sparkles, MessageSquare, Handshake, MapPin } from "lucide-react";
|
||||
import type { Lang } from "@/types/i18n";
|
||||
import { defaultLang } from "@/i18n/ui";
|
||||
import Layout from '@/layouts/Layout.astro';
|
||||
import GlassHeader from '@/components/GlassHeader';
|
||||
import Footer from '@/components/Footer';
|
||||
import Container from '@/components/ui/Container.astro';
|
||||
import type { Lang } from '@/types/i18n';
|
||||
import { defaultLang } from '@/i18n/ui';
|
||||
|
||||
const lang = Astro.currentLocale as Lang || defaultLang;
|
||||
const t = useTranslations(lang);
|
||||
const pageTitle = lang === 'zh' ? '现在' : 'Now';
|
||||
const lang = (Astro.currentLocale as Lang) || defaultLang;
|
||||
const isZh = lang === 'zh';
|
||||
|
||||
const doing = isZh
|
||||
? ['推进工程化案例化的个人站升级', '优化远程协作下的交付流程与文档标准', '持续打磨 AI 协作开发实践']
|
||||
: ['Upgrading this portfolio into an engineering case-study site', 'Improving remote-first delivery workflow and documentation quality', 'Refining practical AI-assisted development workflows'];
|
||||
|
||||
const exploring = isZh
|
||||
? ['复杂权限系统的前端边界设计', '大规模业务表单与状态流管理', '更稳定的跨团队异步协作机制']
|
||||
: ['Frontend boundary design for complex permission systems', 'Scalable state management for large business forms', 'More stable async collaboration practices across teams'];
|
||||
|
||||
const shipping = isZh
|
||||
? ['工程案例库结构重构', '导航与路由转化路径优化', '双语文案统一与信息层级简化']
|
||||
: ['Refactoring project pages into engineering case studies', 'Optimizing navigation and conversion routes', 'Unifying EN/ZH copy and simplifying information hierarchy'];
|
||||
---
|
||||
|
||||
<Layout title={pageTitle}>
|
||||
<Layout title={isZh ? '现在' : 'Now'}>
|
||||
<GlassHeader lang={lang} client:load transition:persist="header" />
|
||||
<main class="min-h-screen relative overflow-hidden">
|
||||
<!-- 背景装饰 -->
|
||||
<div class="absolute inset-0 -z-10">
|
||||
<div class="absolute left-1/2 top-0 h-[800px] w-[1000px] -translate-x-1/2 -translate-y-1/2 bg-primary/10 blur-[120px]"></div>
|
||||
<div class="absolute right-0 top-1/3 h-[500px] w-[500px] bg-blue-500/5 blur-[100px]"></div>
|
||||
</div>
|
||||
|
||||
<!-- Now Page Hero -->
|
||||
<section class="pt-32 pb-24 relative overflow-hidden">
|
||||
<div class="page-hero-overlay" aria-hidden="true"></div>
|
||||
<main class="min-h-screen pt-24 pb-20">
|
||||
<Container>
|
||||
<section class="page-content-main">
|
||||
<h1 class="text-4xl font-bold tracking-tight sm:text-5xl">{isZh ? '现在' : 'Now'}</h1>
|
||||
<p class="mt-4 text-lg text-muted-foreground">{isZh ? '我当前的工作重点与近期交付。' : 'What I am focusing on right now and what I am shipping recently.'}</p>
|
||||
</section>
|
||||
|
||||
<Container className="relative z-10">
|
||||
<div class="max-w-4xl mx-auto text-center">
|
||||
<div class="inline-flex items-center gap-2 px-3 py-1 rounded-full bg-primary/10 border border-primary/20 text-primary text-xs font-bold uppercase tracking-wider mb-6 animate-fade-in">
|
||||
<span class="relative flex h-2 w-2">
|
||||
<span class="animate-ping absolute inline-flex h-full w-full rounded-full bg-primary opacity-75"></span>
|
||||
<span class="relative inline-flex rounded-full h-2 w-2 bg-primary"></span>
|
||||
</span>
|
||||
{lang === 'zh' ? '实时状态' : "Live Status"}
|
||||
</div>
|
||||
<h1 class="page-title-gradient text-6xl md:text-8xl font-bold mb-8 tracking-tight">
|
||||
{lang === 'zh' ? '现在' : 'Now'}
|
||||
</h1>
|
||||
<p class="text-xl md:text-2xl text-muted-foreground leading-relaxed max-w-2xl mx-auto">
|
||||
{lang === 'zh'
|
||||
? '关于我现在正在关注的事、正在构建的产品以及我的生活状态。'
|
||||
: 'What I am currently focusing on, the products I am building, and my life status.'}
|
||||
</p>
|
||||
</div>
|
||||
</Container>
|
||||
</section>
|
||||
<section class="page-content-main mt-10 grid gap-6 md:grid-cols-3">
|
||||
<article class="page-surface p-6">
|
||||
<h2 class="text-lg font-bold">{isZh ? '在做什么' : 'Doing'}</h2>
|
||||
<ul class="mt-4 space-y-2 text-sm text-muted-foreground">
|
||||
{doing.map((item) => <li>• {item}</li>)}
|
||||
</ul>
|
||||
</article>
|
||||
|
||||
<!-- Main Content -->
|
||||
<section class="pb-32 relative mt-12 md:mt-24">
|
||||
<Container>
|
||||
<div class="max-w-4xl mx-auto space-y-24">
|
||||
<!-- Building Section -->
|
||||
<div class="space-y-10">
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="w-12 h-12 bg-primary/10 rounded-xl flex items-center justify-center border border-primary/20">
|
||||
<Hammer className="w-6 h-6 text-primary" />
|
||||
</div>
|
||||
<h2 class="text-3xl md:text-4xl font-bold tracking-tight">
|
||||
{lang === 'zh' ? '正在构建' : 'Building'}
|
||||
</h2>
|
||||
</div>
|
||||
<article class="page-surface p-6">
|
||||
<h2 class="text-lg font-bold">{isZh ? '在研究什么' : 'Exploring'}</h2>
|
||||
<ul class="mt-4 space-y-2 text-sm text-muted-foreground">
|
||||
{exploring.map((item) => <li>• {item}</li>)}
|
||||
</ul>
|
||||
</article>
|
||||
|
||||
<div class="group relative">
|
||||
<div class="absolute -inset-1 rounded-[2rem] bg-gradient-to-r from-primary/30 to-blue-600/30 opacity-20 blur-xl transition duration-500 group-hover:opacity-40"></div>
|
||||
<div class="page-surface relative p-8 md:p-12 overflow-hidden border-primary/10 rounded-[2rem]">
|
||||
<div class="flex flex-col md:flex-row gap-10 items-start">
|
||||
<div class="w-24 h-24 bg-gradient-to-br from-primary to-blue-600 rounded-2xl flex items-center justify-center flex-shrink-0 shadow-lg shadow-primary/20 group-hover:scale-105 transition-transform duration-500">
|
||||
<Zap className="w-12 h-12 text-white" />
|
||||
</div>
|
||||
<div class="flex-1 space-y-6">
|
||||
<div class="flex items-center justify-between">
|
||||
<h3 class="text-3xl font-bold">Elynd</h3>
|
||||
<span class="px-4 py-1.5 bg-green-500/10 text-green-600 dark:text-green-400 rounded-full text-xs font-bold border border-green-500/20 uppercase tracking-wider">Active</span>
|
||||
</div>
|
||||
<p class="text-xl text-muted-foreground leading-relaxed">
|
||||
{lang === 'zh'
|
||||
? '一个开放的 AI 工作空间,让构建者能够更智能地工作。从 AI 第一性原理出发,打造完全开放、可自托管的协作工具。'
|
||||
: 'An open AI workspace for builders to work smarter. Built from first principles of AI, creating fully open, self-hostable collaboration tools.'}
|
||||
</p>
|
||||
<div class="flex flex-wrap gap-3 pt-2">
|
||||
{['AI Product', 'TypeScript', 'Open Source', 'Astro'].map(tag => (
|
||||
<span class="px-4 py-2 bg-secondary/50 text-secondary-foreground rounded-lg text-sm font-medium border border-border/50">{tag}</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<article class="page-surface p-6">
|
||||
<h2 class="text-lg font-bold">{isZh ? '最近交付' : 'Shipping'}</h2>
|
||||
<ul class="mt-4 space-y-2 text-sm text-muted-foreground">
|
||||
{shipping.map((item) => <li>• {item}</li>)}
|
||||
</ul>
|
||||
</article>
|
||||
</section>
|
||||
|
||||
<!-- Exploring Section -->
|
||||
<div class="space-y-10">
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="w-12 h-12 bg-blue-500/10 rounded-xl flex items-center justify-center border border-blue-500/20">
|
||||
<Telescope className="w-6 h-6 text-blue-500" />
|
||||
</div>
|
||||
<h2 class="text-3xl md:text-4xl font-bold tracking-tight">
|
||||
{lang === 'zh' ? '正在探索' : 'Exploring'}
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<div class="grid gap-8 md:grid-cols-2">
|
||||
{[
|
||||
{
|
||||
title: lang === 'zh' ? 'AI Agents 与工作流自动化' : 'AI Agents & Workflow Automation',
|
||||
desc: lang === 'zh' ? '研究如何构建能够自主完成复杂任务的 AI Agent,以及如何将 AI 能力融入日常工作流。' : 'Researching how to build AI Agents that can autonomously complete complex tasks, and how to integrate AI capabilities into daily workflows.',
|
||||
icon: Bot
|
||||
},
|
||||
{
|
||||
title: lang === 'zh' ? '产品导向的开发实践' : 'Product-Led Development Practices',
|
||||
desc: lang === 'zh' ? '探索从产品角度思考开发,构建真正解决用户问题的产品,而不仅仅是技术实现。' : 'Exploring product thinking in development, building products that truly solve user problems, not just technical implementations.',
|
||||
icon: Target
|
||||
}
|
||||
].map(item => (
|
||||
<div class="page-surface p-8 rounded-3xl hover:translate-y-[-4px] transition-all duration-300 border-border/50 group">
|
||||
<div class="w-12 h-12 bg-muted rounded-xl flex items-center justify-center mb-6 group-hover:scale-110 transition-transform duration-300">
|
||||
<item.icon className="w-6 h-6 text-foreground" />
|
||||
</div>
|
||||
<h3 class="text-2xl font-bold mb-4">{item.title}</h3>
|
||||
<p class="text-muted-foreground text-base leading-relaxed">{item.desc}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Shipping Section -->
|
||||
<div class="space-y-10">
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="w-12 h-12 bg-green-500/10 rounded-xl flex items-center justify-center border border-green-500/20">
|
||||
<Rocket className="w-6 h-6 text-green-500" />
|
||||
</div>
|
||||
<h2 class="text-3xl md:text-4xl font-bold tracking-tight">
|
||||
{lang === 'zh' ? '最近发布' : 'Shipping'}
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<div class="space-y-6">
|
||||
{[
|
||||
{
|
||||
title: lang === 'zh' ? '个人网站全新改版' : 'Portfolio Redesign',
|
||||
desc: lang === 'zh' ? '基于 Astro 5 和 Tailwind 4 的高性能双语站点。' : 'High-performance bilingual site built with Astro 5 & Tailwind 4.',
|
||||
icon: Sparkles,
|
||||
color: 'bg-green-500/10',
|
||||
iconColor: 'text-green-500'
|
||||
},
|
||||
{
|
||||
title: lang === 'zh' ? 'DeepSeek 满血版接入' : 'DeepSeek Full Access',
|
||||
desc: lang === 'zh' ? '在本地工作流中深度整合 DeepSeek-V3 提升效率。' : 'Deep integration of DeepSeek-V3 in local workflows for efficiency.',
|
||||
icon: MessageSquare,
|
||||
color: 'bg-blue-500/10',
|
||||
iconColor: 'text-blue-500'
|
||||
}
|
||||
].map(item => (
|
||||
<div class="group relative flex items-center gap-6 p-4 rounded-2xl hover:bg-muted/50 transition-all duration-300">
|
||||
<div class={`w-12 h-12 ${item.color} rounded-xl flex items-center justify-center flex-shrink-0 group-hover:scale-110 transition-transform`}>
|
||||
<item.icon className={`w-6 h-6 ${item.iconColor}`} />
|
||||
</div>
|
||||
<div class="flex-1 min-w-0">
|
||||
<h3 class="font-bold text-lg group-hover:text-primary transition-colors truncate">
|
||||
{item.title}
|
||||
</h3>
|
||||
<p class="text-sm text-muted-foreground line-clamp-1">
|
||||
{item.desc}
|
||||
</p>
|
||||
</div>
|
||||
<div class="text-muted-foreground/30 group-hover:text-primary/50 transition-colors">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12h14"/><path d="m12 5 7 7-7 7"/></svg>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Bottom Grid: Collaboration & Meta -->
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-8 pt-8">
|
||||
<!-- Collaboration Card -->
|
||||
<div class="relative group h-full">
|
||||
<div class="absolute -inset-1 rounded-[2.5rem] bg-gradient-to-br from-yellow-500/20 to-orange-500/20 blur opacity-75"></div>
|
||||
<div class="page-surface relative p-10 border-yellow-500/10 bg-gradient-to-br from-card to-yellow-500/5 rounded-[2.5rem] h-full flex flex-col justify-between">
|
||||
<div>
|
||||
<div class="w-14 h-14 bg-yellow-500/10 rounded-2xl flex items-center justify-center mb-8">
|
||||
<Handshake className="w-8 h-8 text-yellow-600 dark:text-yellow-400" />
|
||||
</div>
|
||||
<h3 class="text-3xl font-bold mb-4">{lang === 'zh' ? '开放合作' : 'Collaborate'}</h3>
|
||||
<p class="text-lg text-muted-foreground mb-8 leading-relaxed">
|
||||
{lang === 'zh'
|
||||
? '始终对有趣的产品想法和技术咨询持开放态度。'
|
||||
: "Always open to interesting product ideas and technical consulting."}
|
||||
</p>
|
||||
</div>
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||
<a href={lang === 'zh' ? '/zh/hire' : '/hire'} class="flex items-center justify-center py-4 rounded-2xl bg-primary text-primary-foreground font-bold text-base hover:scale-[1.02] transition-transform">
|
||||
{lang === 'zh' ? '合作方式' : 'View Options'}
|
||||
</a>
|
||||
<a href="mailto:zhaoguiyang18@gmail.com" class="flex items-center justify-center py-4 rounded-2xl border border-border bg-background/50 text-foreground font-bold text-base hover:bg-muted transition-colors">
|
||||
{lang === 'zh' ? '发送邮件' : 'Email Me'}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Meta/Status Card -->
|
||||
<div class="page-surface p-10 border-border/50 rounded-[2.5rem] flex flex-col items-center justify-center text-center space-y-6">
|
||||
<div class="w-20 h-20 bg-muted rounded-full flex items-center justify-center text-3xl">
|
||||
<MapPin className="w-10 h-10 text-primary" />
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<h3 class="text-2xl font-bold">{lang === 'zh' ? '当前位置' : 'Location'}</h3>
|
||||
<p class="text-muted-foreground">{lang === 'zh' ? '中国 · 杭州' : 'Hangzhou, China'}</p>
|
||||
</div>
|
||||
<div class="pt-4 w-full">
|
||||
<div class="inline-flex items-center gap-3 px-5 py-2.5 rounded-full bg-muted/50 border border-border/50 mx-auto">
|
||||
<span class="text-xs text-muted-foreground uppercase font-bold tracking-widest border-r border-border/50 pr-3">
|
||||
{lang === 'zh' ? '最后更新' : 'Updated'}
|
||||
</span>
|
||||
<span class="text-sm font-semibold">2025.03.14</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Container>
|
||||
</section>
|
||||
<section class="page-content-main mt-8 page-surface p-6">
|
||||
<p class="text-sm text-muted-foreground">
|
||||
{isZh ? '最后更新:2026-03-16' : 'Last updated: 2026-03-16'}
|
||||
</p>
|
||||
</section>
|
||||
</Container>
|
||||
</main>
|
||||
<Footer lang={lang} client:only="react" />
|
||||
|
||||
<Footer lang={lang} client:load />
|
||||
</Layout>
|
||||
|
||||
@@ -1,114 +1,133 @@
|
||||
---
|
||||
import Layout from "@/layouts/Layout.astro";
|
||||
import GlassHeader from "@/components/GlassHeader";
|
||||
import Footer from "@/components/Footer";
|
||||
import Container from "@/components/ui/Container.astro";
|
||||
import ProjectCard from "@/components/ProjectCard.astro";
|
||||
import { useTranslations } from "@/i18n/utils";
|
||||
import type { Lang } from "@/types/i18n";
|
||||
import { defaultLang } from "@/i18n/ui";
|
||||
import { projects } from "@/lib/data/index";
|
||||
import Layout from '@/layouts/Layout.astro';
|
||||
import GlassHeader from '@/components/GlassHeader';
|
||||
import Footer from '@/components/Footer';
|
||||
import Container from '@/components/ui/Container.astro';
|
||||
import { projects } from '@/lib/data';
|
||||
import type { Lang } from '@/types/i18n';
|
||||
import { defaultLang } from '@/i18n/ui';
|
||||
|
||||
// 使用Astro.currentLocale获取当前语言环境
|
||||
const lang = Astro.currentLocale as Lang || defaultLang;
|
||||
const t = useTranslations(lang);
|
||||
const pageTitle = t('projects.title');
|
||||
const lang = (Astro.currentLocale as Lang) || defaultLang;
|
||||
const isZh = lang === 'zh';
|
||||
const pageProjects = projects[lang];
|
||||
|
||||
// 根据当前语言获取项目数据
|
||||
const currentProjects = projects[lang as keyof typeof projects] || projects.en;
|
||||
|
||||
const filterOptions = [
|
||||
{ key: "all", label: t("project.filter.all") },
|
||||
{ key: "product", label: t("project.type.product") },
|
||||
{ key: "client", label: t("project.type.client") },
|
||||
{ key: "experiment", label: t("project.type.experiment") },
|
||||
const filters = [
|
||||
{ key: 'all', label: isZh ? '全部' : 'All' },
|
||||
{ key: 'product', label: isZh ? '产品系统' : 'Product Systems' },
|
||||
{ key: 'client', label: isZh ? '企业项目' : 'Client Systems' },
|
||||
{ key: 'experiment', label: isZh ? '实验项目' : 'Experiments' },
|
||||
];
|
||||
---
|
||||
|
||||
<Layout title={pageTitle}>
|
||||
<Layout title={isZh ? '项目' : 'Projects'}>
|
||||
<GlassHeader lang={lang} client:load transition:persist="header" />
|
||||
<main class="min-h-screen relative overflow-hidden">
|
||||
<div
|
||||
aria-hidden="true"
|
||||
class="page-hero-overlay"
|
||||
></div>
|
||||
|
||||
<section class="relative z-10 py-24">
|
||||
<Container>
|
||||
<div class="text-center mb-8">
|
||||
<h1 class="page-title-gradient text-5xl md:text-6xl font-bold mb-6">
|
||||
{t('projects.title')}
|
||||
</h1>
|
||||
<p class="text-lg text-muted-foreground page-content-main mb-12 leading-relaxed">
|
||||
{t('projects.slogan')} {t('projects.description')}
|
||||
</p>
|
||||
<main class="min-h-screen pt-24 pb-20" data-projects-root>
|
||||
<Container>
|
||||
<section class="page-content-main">
|
||||
<h1 class="text-4xl font-bold tracking-tight sm:text-5xl">{isZh ? '工程案例' : 'Engineering Case Studies'}</h1>
|
||||
<p class="mt-4 text-lg leading-relaxed text-muted-foreground">
|
||||
{isZh
|
||||
? '围绕复杂系统建设的真实项目经验,重点展示技术挑战、职责边界和结果。'
|
||||
: 'Real projects focused on complex system delivery, highlighting technical challenges, responsibilities, and outcomes.'}
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section class="page-content-main mt-8">
|
||||
<div class="page-surface mb-8 flex flex-wrap gap-2 p-2">
|
||||
{filters.map((filter, index) => (
|
||||
<button
|
||||
type="button"
|
||||
data-filter={filter.key}
|
||||
aria-pressed={index === 0 ? 'true' : 'false'}
|
||||
class={`rounded-md px-4 py-2 text-sm font-semibold ${index === 0 ? 'bg-primary text-primary-foreground' : 'text-muted-foreground hover:bg-muted hover:text-foreground'}`}
|
||||
>
|
||||
{filter.label}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</Container>
|
||||
</section>
|
||||
|
||||
<section class="relative z-10 pb-20" data-projects-root>
|
||||
<Container>
|
||||
<div class="page-content-main">
|
||||
<div class="page-surface flex flex-wrap gap-2 mb-8 p-1 rounded-xl">
|
||||
{filterOptions.map((option, index) => (
|
||||
<button
|
||||
type="button"
|
||||
data-filter={option.key}
|
||||
aria-pressed={index === 0 ? "true" : "false"}
|
||||
class={`rounded-lg px-4 py-2 text-sm font-medium transition-colors ${
|
||||
index === 0
|
||||
? "bg-primary text-primary-foreground"
|
||||
: "text-muted-foreground hover:text-foreground hover:bg-muted"
|
||||
}`}
|
||||
>
|
||||
{option.label}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
<div class="space-y-6">
|
||||
{pageProjects.map((project) => (
|
||||
<article data-project-card data-type={project.type} class="page-surface p-6 md:p-8">
|
||||
<div class="grid gap-6 lg:grid-cols-[1.2fr_1fr]">
|
||||
<div>
|
||||
<p class="text-xs font-semibold uppercase tracking-wider text-primary">{project.systemType}</p>
|
||||
<h2 class="mt-2 text-2xl font-bold tracking-tight">{project.title}</h2>
|
||||
<p class="mt-3 text-muted-foreground">{project.context}</p>
|
||||
|
||||
<div class="grid gap-6 md:grid-cols-2 lg:grid-cols-3">
|
||||
{currentProjects.map((project) => (
|
||||
<ProjectCard project={project} lang={lang} showStatus={true} />
|
||||
))}
|
||||
</div>
|
||||
<div class="mt-5 flex flex-wrap gap-2">
|
||||
{project.tech.map((tech) => (
|
||||
<span class="rounded-md border border-border bg-muted/50 px-2.5 py-1 text-xs font-medium">{tech}</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="space-y-4">
|
||||
<div>
|
||||
<h3 class="text-sm font-bold uppercase tracking-wider text-foreground/80">{isZh ? '技术挑战' : 'Technical Challenges'}</h3>
|
||||
<ul class="mt-2 space-y-1.5 text-sm text-muted-foreground">
|
||||
{project.challenges?.map((item) => <li>• {item}</li>)}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3 class="text-sm font-bold uppercase tracking-wider text-foreground/80">{isZh ? '职责范围' : 'Responsibilities'}</h3>
|
||||
<ul class="mt-2 space-y-1.5 text-sm text-muted-foreground">
|
||||
{project.responsibilities?.map((item) => <li>• {item}</li>)}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3 class="text-sm font-bold uppercase tracking-wider text-foreground/80">{isZh ? '结果' : 'Outcomes'}</h3>
|
||||
<ul class="mt-2 space-y-1.5 text-sm text-foreground/90">
|
||||
{project.outcomes?.map((item) => <li>• {item}</li>)}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
))}
|
||||
</div>
|
||||
</Container>
|
||||
</section>
|
||||
</section>
|
||||
</Container>
|
||||
</main>
|
||||
|
||||
<Footer lang={lang} client:load />
|
||||
</Layout>
|
||||
|
||||
<script>
|
||||
const projectsRoot = document.querySelector("[data-projects-root]");
|
||||
if (projectsRoot) {
|
||||
const filterButtons = Array.from(projectsRoot.querySelectorAll("[data-filter]"));
|
||||
const projectCards = Array.from(projectsRoot.querySelectorAll("[data-project-card]"));
|
||||
const root = document.querySelector('[data-projects-root]');
|
||||
if (!root) {
|
||||
// no-op
|
||||
} else {
|
||||
const buttons = Array.from(root.querySelectorAll('[data-filter]'));
|
||||
const cards = Array.from(root.querySelectorAll('[data-project-card]'));
|
||||
|
||||
const applyFilter = (activeFilter: string | null) => {
|
||||
projectCards.forEach((card) => {
|
||||
const projectType = card.getAttribute("data-type");
|
||||
const shouldShow = activeFilter === "all" || projectType === activeFilter;
|
||||
card.classList.toggle("hidden", !shouldShow);
|
||||
const apply = (activeFilter) => {
|
||||
cards.forEach((card) => {
|
||||
const cardType = card.getAttribute('data-type');
|
||||
const visible = activeFilter === 'all' || activeFilter === cardType;
|
||||
card.classList.toggle('hidden', !visible);
|
||||
});
|
||||
|
||||
filterButtons.forEach((button) => {
|
||||
const isActive = button.getAttribute("data-filter") === activeFilter;
|
||||
button.setAttribute("aria-pressed", isActive ? "true" : "false");
|
||||
button.classList.toggle("bg-primary", isActive);
|
||||
button.classList.toggle("text-primary-foreground", isActive);
|
||||
button.classList.toggle("text-muted-foreground", !isActive);
|
||||
button.classList.toggle("hover:text-foreground", !isActive);
|
||||
button.classList.toggle("hover:bg-muted", !isActive);
|
||||
buttons.forEach((button) => {
|
||||
const isActive = button.getAttribute('data-filter') === activeFilter;
|
||||
button.setAttribute('aria-pressed', isActive ? 'true' : 'false');
|
||||
button.classList.toggle('bg-primary', isActive);
|
||||
button.classList.toggle('text-primary-foreground', isActive);
|
||||
button.classList.toggle('text-muted-foreground', !isActive);
|
||||
button.classList.toggle('hover:bg-muted', !isActive);
|
||||
button.classList.toggle('hover:text-foreground', !isActive);
|
||||
});
|
||||
};
|
||||
|
||||
filterButtons.forEach((button) => {
|
||||
button.addEventListener("click", () => {
|
||||
const selectedFilter = button.getAttribute("data-filter") ?? "all";
|
||||
applyFilter(selectedFilter);
|
||||
buttons.forEach((button) => {
|
||||
button.addEventListener('click', () => {
|
||||
apply(button.getAttribute('data-filter') ?? 'all');
|
||||
});
|
||||
});
|
||||
|
||||
applyFilter("all");
|
||||
apply('all');
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1,42 +1,14 @@
|
||||
---
|
||||
title: "What I Do"
|
||||
description: "Professional services focused on building AI products and technical excellence"
|
||||
title: "Moved to Uses"
|
||||
description: "This page has moved."
|
||||
layout: "../layouts/AboutLayout.astro"
|
||||
---
|
||||
|
||||
import HighlightBox from '../components/markdown/HighlightBox.astro';
|
||||
# This page moved
|
||||
|
||||
# What I Do 🚀
|
||||
The old **Services** page has been replaced by focused pages:
|
||||
|
||||
<HighlightBox type="tip">
|
||||
Building AI-powered products and turning ambitious ideas into reality.
|
||||
</HighlightBox>
|
||||
- [Uses](/uses)
|
||||
- [Contact](/contact)
|
||||
|
||||
## AI Product Development 🤖
|
||||
|
||||
- Building AI-powered products from concept to launch
|
||||
- RAG applications, AI agents, and intelligent automation
|
||||
- Full-stack development with modern tech stack (React, Next.js, Node.js)
|
||||
- Product thinking - focus on solving real problems
|
||||
- Rapid prototyping and iterative development
|
||||
|
||||
## Technical Consulting 📊
|
||||
|
||||
<HighlightBox type="info" title="How I Can Help">
|
||||
|
||||
- Technical architecture and stack decisions
|
||||
- Code reviews and best practices implementation
|
||||
- Team mentoring and knowledge transfer
|
||||
- Performance optimization and scaling strategies
|
||||
- Technical due diligence for investments
|
||||
|
||||
</HighlightBox>
|
||||
|
||||
# Let's Build Together 💼
|
||||
|
||||
<HighlightBox type="success" title="Get in Touch">
|
||||
I'm always open to discussing new projects and opportunities. Whether you're looking for a technical co-founder, need help building an AI product, or want technical consulting - let's talk.
|
||||
|
||||
- Email: **zhaoguiyang18@gmail.com**
|
||||
- Check out my [Hire Me](/hire) page for collaboration options
|
||||
</HighlightBox>
|
||||
If you are hiring for a remote role, please start from [Contact](/contact).
|
||||
|
||||
40
src/pages/uses.astro
Normal file
40
src/pages/uses.astro
Normal file
@@ -0,0 +1,40 @@
|
||||
---
|
||||
import Layout from '@/layouts/Layout.astro';
|
||||
import GlassHeader from '@/components/GlassHeader';
|
||||
import Footer from '@/components/Footer';
|
||||
import Container from '@/components/ui/Container.astro';
|
||||
import { uses } from '@/lib/data';
|
||||
import type { Lang } from '@/types/i18n';
|
||||
import { defaultLang } from '@/i18n/ui';
|
||||
|
||||
const lang = (Astro.currentLocale as Lang) || defaultLang;
|
||||
const isZh = lang === 'zh';
|
||||
---
|
||||
|
||||
<Layout title={isZh ? '工具' : 'Uses'}>
|
||||
<GlassHeader lang={lang} client:load transition:persist="header" />
|
||||
|
||||
<main class="min-h-screen pt-24 pb-20">
|
||||
<Container>
|
||||
<section class="page-content-main">
|
||||
<h1 class="text-4xl font-bold tracking-tight sm:text-5xl">{isZh ? '工具与工作流' : 'Uses'}</h1>
|
||||
<p class="mt-4 text-lg text-muted-foreground">
|
||||
{isZh ? '我在日常研发中稳定使用的工具与协作方式。' : 'Tools and workflows I use for daily engineering work.'}
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section class="page-content-main mt-10 grid gap-6 md:grid-cols-2">
|
||||
{uses.map((group) => (
|
||||
<article class="page-surface p-6">
|
||||
<h2 class="text-lg font-bold">{group.title[lang]}</h2>
|
||||
<ul class="mt-4 space-y-2 text-sm text-muted-foreground">
|
||||
{group.items.map((item) => <li>• {item}</li>)}
|
||||
</ul>
|
||||
</article>
|
||||
))}
|
||||
</section>
|
||||
</Container>
|
||||
</main>
|
||||
|
||||
<Footer lang={lang} client:load />
|
||||
</Layout>
|
||||
@@ -1,220 +1,70 @@
|
||||
---
|
||||
import Layout from "@/layouts/Layout.astro";
|
||||
import GlassHeader from "@/components/GlassHeader";
|
||||
import Footer from "@/components/Footer";
|
||||
import Container from "@/components/ui/Container.astro";
|
||||
import HighlightBox from "@/components/markdown/HighlightBox.astro";
|
||||
import { useTranslations } from "@/i18n/utils";
|
||||
import type { Lang } from "@/types/i18n";
|
||||
import { defaultLang } from "@/i18n/ui";
|
||||
import {
|
||||
Sparkles,
|
||||
Heart,
|
||||
Zap,
|
||||
Mail,
|
||||
MessageSquare,
|
||||
Send,
|
||||
Github,
|
||||
Twitter,
|
||||
Linkedin,
|
||||
Globe
|
||||
} from "lucide-react";
|
||||
import Layout from '@/layouts/Layout.astro';
|
||||
import GlassHeader from '@/components/GlassHeader';
|
||||
import Footer from '@/components/Footer';
|
||||
import Container from '@/components/ui/Container.astro';
|
||||
import { personalInfo } from '@/lib/data';
|
||||
import type { Lang } from '@/types/i18n';
|
||||
import { defaultLang } from '@/i18n/ui';
|
||||
|
||||
const IconMap: Record<string, any> = {
|
||||
mail: Mail,
|
||||
wechat: MessageSquare,
|
||||
qq: MessageSquare, // Lucide doesn't have QQ, using MessageSquare
|
||||
send: Send,
|
||||
github: Github,
|
||||
twitter: Twitter,
|
||||
linkedin: Linkedin,
|
||||
globe: Globe,
|
||||
};
|
||||
const lang = (Astro.currentLocale as Lang) || defaultLang;
|
||||
const isZh = lang === 'zh';
|
||||
|
||||
const lang = (Astro.currentLocale as Lang) || 'zh';
|
||||
const t = useTranslations(lang);
|
||||
const pageTitle = t("about.title");
|
||||
const focusAreas = isZh
|
||||
? ['前端架构设计与模块治理', '大型企业应用工程化交付', '金融与区块链系统前端建设', 'AI 协作开发流程实践']
|
||||
: ['Frontend architecture and module governance', 'Large-scale enterprise application delivery', 'Financial and blockchain system engineering', 'AI-assisted development workflow practices'];
|
||||
|
||||
const experienceNotes = isZh
|
||||
? [
|
||||
'8 年专业开发经验,持续参与复杂业务系统建设。',
|
||||
'参与多个政府与金融科技相关系统,包括交易平台、区块链基础设施、产业管理系统。',
|
||||
'擅长跨角色协作与远程异步沟通,注重稳定交付与长期可维护性。',
|
||||
]
|
||||
: [
|
||||
'8 years of professional development experience across complex business systems.',
|
||||
'Contributed to government- and fintech-related systems, including trading platforms, blockchain infrastructure, and industrial management systems.',
|
||||
'Strong in cross-functional collaboration and remote async execution with long-term maintainability in mind.',
|
||||
];
|
||||
---
|
||||
|
||||
<Layout title={pageTitle}>
|
||||
<GlassHeader client:load transition:persist="header" lang={lang} />
|
||||
|
||||
<main class="min-h-screen relative overflow-hidden pt-16 sm:pt-20">
|
||||
<!-- Background Decor -->
|
||||
<div class="fixed inset-0 -z-10 h-full w-full bg-background">
|
||||
<div class="absolute inset-0 bg-gradient-to-br from-primary/5 via-transparent to-primary/5 dark:from-primary/10 dark:via-transparent dark:to-primary/5"></div>
|
||||
</div>
|
||||
<Layout title={isZh ? '关于' : 'About'}>
|
||||
<GlassHeader lang={lang} client:load transition:persist="header" />
|
||||
|
||||
<!-- Hero Section -->
|
||||
<section class="relative z-10 py-16 md:py-24">
|
||||
<Container>
|
||||
<div class="max-w-4xl mx-auto">
|
||||
<div class="flex flex-col md:flex-row items-center gap-12 mb-16">
|
||||
<div class="relative group">
|
||||
<div class="absolute -inset-1 rounded-full bg-gradient-to-r from-primary to-purple-600 opacity-75 blur transition duration-1000 group-hover:opacity-100 group-hover:duration-200"></div>
|
||||
<div class="relative h-48 w-48 rounded-full border-4 border-background overflow-hidden bg-muted">
|
||||
<img
|
||||
src="/avatar.png"
|
||||
alt="Joey Zhao"
|
||||
class="h-full w-full object-cover transition-transform duration-500 group-hover:scale-110"
|
||||
onerror="this.src='https://ui-avatars.com/api/?name=Joy+Zhao&background=0D8ABC&color=fff&size=200'"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="text-center md:text-left space-y-6">
|
||||
<h1 class="text-4xl md:text-6xl font-bold tracking-tight">
|
||||
<span class="bg-gradient-to-r from-foreground to-foreground/70 bg-clip-text text-transparent">
|
||||
{t('about.title')}
|
||||
</span>
|
||||
</h1>
|
||||
<p class="text-xl text-muted-foreground leading-relaxed">
|
||||
{t('about.description')}
|
||||
</p>
|
||||
</div>
|
||||
<main class="min-h-screen pt-24 pb-20">
|
||||
<Container>
|
||||
<section class="page-content-main">
|
||||
<h1 class="text-4xl font-bold tracking-tight sm:text-5xl">{isZh ? '关于我' : 'About'}</h1>
|
||||
<p class="mt-6 text-lg leading-relaxed text-muted-foreground">{personalInfo.description[lang]}</p>
|
||||
</section>
|
||||
|
||||
<section class="page-content-main mt-12 grid gap-6 lg:grid-cols-2">
|
||||
<article class="page-surface p-8">
|
||||
<h2 class="text-2xl font-bold tracking-tight">{isZh ? '背景' : 'Background'}</h2>
|
||||
<div class="mt-4 space-y-4 text-muted-foreground">
|
||||
{personalInfo.about[lang].map((line) => <p>{line}</p>)}
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<div class="grid gap-8">
|
||||
<!-- Intro Box -->
|
||||
<HighlightBox type="tip" title={t('about.intro.title')}>
|
||||
<div class="space-y-4 py-2">
|
||||
<p class="text-lg leading-relaxed whitespace-pre-line">{t('about.intro.content')}</p>
|
||||
<div class="p-4 rounded-xl bg-primary/5 border border-primary/10 italic text-primary/80">
|
||||
{t('about.intro.belief')}
|
||||
</div>
|
||||
</div>
|
||||
</HighlightBox>
|
||||
<article class="page-surface p-8">
|
||||
<h2 class="text-2xl font-bold tracking-tight">{isZh ? '技术焦点' : 'Technical Focus'}</h2>
|
||||
<ul class="mt-4 space-y-3 text-muted-foreground">
|
||||
{focusAreas.map((item) => (
|
||||
<li class="flex gap-3"><span class="mt-2 h-1.5 w-1.5 rounded-full bg-primary"></span><span>{item}</span></li>
|
||||
))}
|
||||
</ul>
|
||||
</article>
|
||||
</section>
|
||||
|
||||
<!-- Self Intro -->
|
||||
<div class="page-surface p-8 rounded-2xl border bg-card/40 backdrop-blur-md space-y-6">
|
||||
<h2 class="text-2xl font-bold flex items-center gap-3">
|
||||
<Sparkles className="w-6 h-6 text-primary" />
|
||||
{t('about.me.title')}
|
||||
</h2>
|
||||
<div class="prose dark:prose-invert max-w-none">
|
||||
<p class="text-lg leading-relaxed text-muted-foreground whitespace-pre-line">
|
||||
{t('about.me.content')}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Skills Grid -->
|
||||
<div class="grid md:grid-cols-2 gap-8">
|
||||
<div class="page-surface p-8 rounded-2xl border bg-card/40 backdrop-blur-md space-y-6">
|
||||
<h2 class="text-2xl font-bold flex items-center gap-3">
|
||||
<Zap className="w-6 h-6 text-yellow-500" />
|
||||
{t('about.skills.mastered.title')}
|
||||
</h2>
|
||||
<div class="flex flex-wrap gap-2">
|
||||
{(t('about.skills.mastered.items') as unknown as string[]).map((item: string) => (
|
||||
<div class="px-3 py-1.5 rounded-lg bg-primary/5 border border-primary/10 text-muted-foreground text-xs font-medium">
|
||||
{item}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="page-surface p-8 rounded-2xl border bg-card/40 backdrop-blur-md space-y-6">
|
||||
<h2 class="text-2xl font-bold flex items-center gap-3">
|
||||
<Heart className="w-6 h-6 text-red-500" />
|
||||
{t('about.skills.learning.title')}
|
||||
</h2>
|
||||
<div class="flex flex-wrap gap-2">
|
||||
{(t('about.skills.learning.items') as unknown as string[]).map((item: string) => (
|
||||
<div class="px-3 py-1.5 rounded-lg bg-muted/30 border border-border/50 text-muted-foreground/70 text-xs font-medium">
|
||||
{item}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Interests -->
|
||||
<div class="space-y-8">
|
||||
<h2 class="text-2xl font-bold flex items-center gap-3 px-2">
|
||||
<Heart className="w-6 h-6 text-red-500" />
|
||||
{t('about.interests.title')}
|
||||
</h2>
|
||||
<ul class="grid sm:grid-cols-2 gap-4">
|
||||
{(t('about.interests.items') as unknown as any[]).map((item) => (
|
||||
<li class="flex items-start gap-4 p-4 rounded-xl border border-border/40 bg-card/20 hover:bg-card/40 transition-colors group">
|
||||
<div class="mt-1.5 w-1.5 h-1.5 rounded-full bg-primary/40 group-hover:bg-primary transition-colors shrink-0" />
|
||||
<div class="space-y-1">
|
||||
<span class="font-bold text-foreground">{item.title}</span>
|
||||
<p class="text-sm text-muted-foreground leading-relaxed">{item.content}</p>
|
||||
</div>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- Socials & Community -->
|
||||
<div class="space-y-6">
|
||||
<h2 class="text-2xl font-bold flex items-center gap-3 px-2">
|
||||
<Globe className="w-6 h-6 text-primary" />
|
||||
{t('about.socials.title')}
|
||||
</h2>
|
||||
<div class="flex flex-wrap gap-4 px-2">
|
||||
{(t('about.socials.items') as unknown as any[]).map((item) => {
|
||||
const Icon = IconMap[item.icon] || Globe;
|
||||
return (
|
||||
<a
|
||||
href={item.link}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="flex items-center gap-2 px-4 py-2 rounded-lg bg-muted/30 border border-border/50 hover:border-primary/50 hover:bg-primary/5 transition-all group text-sm font-medium"
|
||||
>
|
||||
<Icon className="w-4 h-4 text-muted-foreground group-hover:text-primary transition-colors" />
|
||||
<span>{item.label}</span>
|
||||
</a>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Contact -->
|
||||
<div class="space-y-8 py-8">
|
||||
<div class="px-2 space-y-4">
|
||||
<h2 class="text-2xl font-bold flex items-center gap-3">
|
||||
<Mail className="w-6 h-6 text-primary" />
|
||||
{t('about.contact.title')}
|
||||
</h2>
|
||||
<p class="text-muted-foreground leading-relaxed max-w-2xl">
|
||||
{t('about.contact.warning')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||
{(t('about.contact.methods') as unknown as any[]).map((method) => {
|
||||
const Icon = IconMap[method.icon] || MessageSquare;
|
||||
const content = (
|
||||
<div class="flex items-center gap-4 p-4 rounded-xl border border-border/40 bg-card/20 hover:bg-card/40 transition-all group">
|
||||
<div class="p-2.5 rounded-lg bg-primary/5 text-primary/70 group-hover:bg-primary/10 group-hover:text-primary transition-colors">
|
||||
<Icon className="w-5 h-5" />
|
||||
</div>
|
||||
<div class="flex flex-col">
|
||||
<span class="text-[10px] font-bold text-muted-foreground uppercase tracking-widest">{method.label}</span>
|
||||
<span class="text-sm font-medium break-all">{method.value}</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
if (method.link) {
|
||||
return (
|
||||
<a href={method.link} class="block transition-transform hover:-translate-y-0.5">
|
||||
{content}
|
||||
</a>
|
||||
);
|
||||
}
|
||||
return <div class="cursor-default">{content}</div>;
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Container>
|
||||
</section>
|
||||
<section class="page-content-main mt-6 page-surface p-8">
|
||||
<h2 class="text-2xl font-bold tracking-tight">{isZh ? '经验概览' : 'Experience'}</h2>
|
||||
<ul class="mt-4 space-y-3 text-muted-foreground">
|
||||
{experienceNotes.map((item) => (
|
||||
<li class="flex gap-3"><span class="mt-2 h-1.5 w-1.5 rounded-full bg-primary"></span><span>{item}</span></li>
|
||||
))}
|
||||
</ul>
|
||||
</section>
|
||||
</Container>
|
||||
</main>
|
||||
|
||||
<Footer client:load lang={lang} />
|
||||
|
||||
<Footer lang={lang} client:load />
|
||||
</Layout>
|
||||
|
||||
@@ -55,16 +55,16 @@ const tags = extractTags(allPostsArray);
|
||||
|
||||
---
|
||||
|
||||
<BlogLayout title="博客 - Joey Zhao" description="关于 AI 产品、全栈开发和公开构建的思考。探索技术与产品构建的交汇点。">
|
||||
<BlogLayout title="写作 - Joey Zhao" description="聚焦架构设计、工程交付与 AI 协作开发实践的技术写作。">
|
||||
<main class="min-h-screen">
|
||||
<!-- 头部区域 -->
|
||||
<Container className="pt-24 pb-12">
|
||||
<div class="text-center mb-16">
|
||||
<h1 class="text-5xl md:text-6xl font-bold bg-gradient-to-r from-foreground via-primary to-primary dark:from-foreground dark:via-blue-200 dark:to-orange-300 bg-clip-text text-transparent mb-6">
|
||||
<span class="text-primary">博客</span>
|
||||
<span class="text-primary">写作</span>
|
||||
</h1>
|
||||
<p class="text-xl text-muted-foreground page-content-main">
|
||||
关于 AI 产品、全栈开发和公开构建的思考。探索技术与产品构建的交汇点。
|
||||
聚焦架构设计、工程交付与 AI 协作开发实践的技术写作。
|
||||
</p>
|
||||
</div>
|
||||
</Container>
|
||||
|
||||
62
src/pages/zh/contact.astro
Normal file
62
src/pages/zh/contact.astro
Normal file
@@ -0,0 +1,62 @@
|
||||
---
|
||||
import Layout from '@/layouts/Layout.astro';
|
||||
import GlassHeader from '@/components/GlassHeader';
|
||||
import Footer from '@/components/Footer';
|
||||
import Container from '@/components/ui/Container.astro';
|
||||
import { contactIntents, contactMethods } from '@/lib/data';
|
||||
import type { Lang } from '@/types/i18n';
|
||||
import { defaultLang } from '@/i18n/ui';
|
||||
|
||||
const lang = (Astro.currentLocale as Lang) || defaultLang;
|
||||
const isZh = lang === 'zh';
|
||||
---
|
||||
|
||||
<Layout title={isZh ? '联系' : 'Contact'}>
|
||||
<GlassHeader lang={lang} client:load transition:persist="header" />
|
||||
|
||||
<main class="min-h-screen pt-24 pb-20">
|
||||
<Container>
|
||||
<section class="page-content-main">
|
||||
<h1 class="text-4xl font-bold tracking-tight sm:text-5xl">{isZh ? '联系' : 'Contact'}</h1>
|
||||
<p class="mt-4 text-lg text-muted-foreground">
|
||||
{isZh
|
||||
? '欢迎联系我沟通远程岗位或项目合作。默认优先响应远程岗位机会。'
|
||||
: 'Open to remote role opportunities and project collaboration. Remote role discussions are prioritized.'}
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section class="page-content-main mt-10 grid gap-6 md:grid-cols-2">
|
||||
{contactIntents.map((intent) => (
|
||||
<article id={intent.id} class="page-surface p-6">
|
||||
<h2 class="text-xl font-bold">{intent.title[lang]}</h2>
|
||||
<p class="mt-3 text-muted-foreground">{intent.description[lang]}</p>
|
||||
</article>
|
||||
))}
|
||||
</section>
|
||||
|
||||
<section class="page-content-main mt-6 page-surface p-6">
|
||||
<h2 class="text-xl font-bold">{isZh ? '联系方式' : 'Contact Methods'}</h2>
|
||||
<ul class="mt-4 space-y-3 text-sm">
|
||||
{contactMethods.map((method) => (
|
||||
<li class="flex flex-col gap-1 sm:flex-row sm:items-center sm:justify-between border-b border-border/70 pb-3">
|
||||
<span class="font-semibold text-foreground">{method.label[lang]}</span>
|
||||
{method.href ? (
|
||||
<a href={method.href} target="_blank" rel="noopener noreferrer" class="text-primary hover:text-primary/80 break-all">{method.value}</a>
|
||||
) : (
|
||||
<span class="text-muted-foreground break-all">{method.value}</span>
|
||||
)}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
|
||||
<p class="mt-5 text-xs text-muted-foreground">
|
||||
{isZh
|
||||
? '联系建议:请在邮件中注明岗位/项目背景、期望合作方式与时间窗口。'
|
||||
: 'Suggestion: include role/project context, expected collaboration model, and timeline in your first message.'}
|
||||
</p>
|
||||
</section>
|
||||
</Container>
|
||||
</main>
|
||||
|
||||
<Footer lang={lang} client:load />
|
||||
</Layout>
|
||||
@@ -1,333 +1,3 @@
|
||||
---
|
||||
import Layout from "@/layouts/Layout.astro";
|
||||
import GlassHeader from "@/components/GlassHeader";
|
||||
import Footer from "@/components/Footer";
|
||||
import Container from "@/components/ui/Container.astro";
|
||||
import { useTranslations } from "@/i18n/utils";
|
||||
import { Users, Wrench, Lightbulb, Handshake, CircleDollarSign, Clock, Target, Check, Calendar, Mail, Radio, Zap, Globe, Smartphone, ShoppingCart, Layout as LayoutIcon, ClipboardList } from "lucide-react";
|
||||
import type { Lang } from "@/types/i18n";
|
||||
import { defaultLang } from "@/i18n/ui";
|
||||
|
||||
const lang = Astro.currentLocale as Lang || defaultLang;
|
||||
const t = useTranslations(lang);
|
||||
const pageTitle = lang === 'zh' ? '合作' : 'Hire Me';
|
||||
return Astro.redirect('/zh/contact', 301);
|
||||
---
|
||||
|
||||
<Layout title={pageTitle}>
|
||||
<GlassHeader lang={lang} client:load transition:persist="header" />
|
||||
<main class="min-h-screen">
|
||||
<!-- Hire Page Hero -->
|
||||
<section class="py-24 relative overflow-hidden">
|
||||
<div class="page-hero-overlay"></div>
|
||||
|
||||
<Container className="relative z-10">
|
||||
<div class="text-center mb-16">
|
||||
<span class="inline-block px-4 py-1 bg-primary/20 text-primary dark:text-primary rounded-full text-sm font-medium mb-4">
|
||||
专业合作
|
||||
</span>
|
||||
<h1 class="page-title-gradient text-5xl md:text-6xl font-bold mb-6">
|
||||
合作
|
||||
</h1>
|
||||
<p class="text-lg text-muted-foreground page-content-narrow">
|
||||
构建优秀产品,从找到合适的技术合伙人开始
|
||||
</p>
|
||||
</div>
|
||||
</Container>
|
||||
</section>
|
||||
|
||||
<!-- Who I Work With -->
|
||||
<section class="py-16 relative">
|
||||
<Container>
|
||||
<div class="page-content-main">
|
||||
<div class="flex items-center gap-4 mb-8">
|
||||
<div class="w-12 h-12 bg-primary/20 rounded-xl flex items-center justify-center">
|
||||
<Users className="w-6 h-6 text-primary" />
|
||||
</div>
|
||||
<h2 class="text-3xl font-bold">
|
||||
我与谁合作
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<div class="grid md:grid-cols-2 gap-6">
|
||||
<div class="page-surface p-6">
|
||||
<h3 class="text-lg font-bold mb-3">
|
||||
初创公司
|
||||
</h3>
|
||||
<p class="text-muted-foreground">
|
||||
从零到一构建产品的早期创业团队,需要快速迭代和灵活响应的技术方案。
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="page-surface p-6">
|
||||
<h3 class="text-lg font-bold mb-3">
|
||||
独立创始人
|
||||
</h3>
|
||||
<p class="text-muted-foreground">
|
||||
独自构建产品的创业者,需要技术合伙人来实现产品愿景。
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="page-surface p-6">
|
||||
<h3 class="text-lg font-bold mb-3">
|
||||
传统企业
|
||||
</h3>
|
||||
<p class="text-muted-foreground">
|
||||
希望数字化转型的传统企业,需要现代化的技术解决方案。
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="page-surface p-6">
|
||||
<h3 class="text-lg font-bold mb-3">
|
||||
技术团队
|
||||
</h3>
|
||||
<p class="text-muted-foreground">
|
||||
需要额外技术资源或特定技能集来推进项目的开发团队。
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Container>
|
||||
</section>
|
||||
|
||||
<!-- What I Build -->
|
||||
<section class="py-16 relative page-section-soft">
|
||||
<Container>
|
||||
<div class="page-content-main">
|
||||
<div class="flex items-center gap-4 mb-8">
|
||||
<div class="w-12 h-12 bg-blue-500/20 rounded-xl flex items-center justify-center">
|
||||
<Wrench className="w-6 h-6 text-blue-500" />
|
||||
</div>
|
||||
<h2 class="text-3xl font-bold">
|
||||
我能构建什么
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<div class="space-y-6">
|
||||
<div class="page-surface p-8">
|
||||
<div class="flex items-start gap-4">
|
||||
<div class="w-12 h-12 bg-gradient-to-br from-primary to-blue-600 rounded-xl flex items-center justify-center flex-shrink-0">
|
||||
<Zap className="w-6 h-6 text-white" />
|
||||
</div>
|
||||
<div>
|
||||
<h3 class="text-xl font-bold mb-2">AI 产品与应用</h3>
|
||||
<p class="text-muted-foreground">
|
||||
从 AI 助手到 Agent 系统,从 RAG 应用到 AI 驱动的 SaaS 产品。我可以帮助你将 AI 能力产品化。
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="page-surface p-8">
|
||||
<div class="flex items-start gap-4">
|
||||
<div class="w-12 h-12 bg-gradient-to-br from-green-600 to-teal-600 rounded-xl flex items-center justify-center flex-shrink-0">
|
||||
<Globe className="w-6 h-6 text-white" />
|
||||
</div>
|
||||
<div>
|
||||
<h3 class="text-xl font-bold mb-2">Web 应用</h3>
|
||||
<p class="text-muted-foreground">
|
||||
现代 Web 应用、SaaS 平台、电商系统、内容管理系统等。使用 React、Next.js、Node.js 等技术栈。
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="page-surface p-8">
|
||||
<div class="flex items-start gap-4">
|
||||
<div class="w-12 h-12 bg-gradient-to-br from-orange-600 to-red-600 rounded-xl flex items-center justify-center flex-shrink-0">
|
||||
<Smartphone className="w-6 h-6 text-white" />
|
||||
</div>
|
||||
<div>
|
||||
<h3 class="text-xl font-bold mb-2">全栈产品</h3>
|
||||
<p class="text-muted-foreground">
|
||||
从前端到后端,从数据库到部署,我提供完整的全栈开发能力,一个项目只需一个开发者。
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Container>
|
||||
</section>
|
||||
|
||||
<!-- Engagement Models -->
|
||||
<section class="py-16 relative">
|
||||
<Container>
|
||||
<div class="page-content-main">
|
||||
<div class="flex items-center gap-4 mb-8">
|
||||
<div class="w-12 h-12 bg-green-500/20 rounded-xl flex items-center justify-center">
|
||||
<ClipboardList className="w-6 h-6 text-green-500" />
|
||||
</div>
|
||||
<h2 class="text-3xl font-bold">
|
||||
合作模式
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<div class="grid md:grid-cols-3 gap-6">
|
||||
<div class="page-surface p-6 text-center">
|
||||
<div class="w-12 h-12 bg-green-500/20 rounded-xl flex items-center justify-center mx-auto mb-4">
|
||||
<Handshake className="w-6 h-6 text-green-600 dark:text-green-400" />
|
||||
</div>
|
||||
<h3 class="text-lg font-bold mb-2">
|
||||
技术合伙人
|
||||
</h3>
|
||||
<p class="text-sm text-muted-foreground">
|
||||
长期合作,股权合作,共同承担创业风险与回报
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="page-surface p-6 text-center">
|
||||
<div class="w-12 h-12 bg-blue-500/20 rounded-xl flex items-center justify-center mx-auto mb-4">
|
||||
<CircleDollarSign className="w-6 h-6 text-blue-600 dark:text-blue-400" />
|
||||
</div>
|
||||
<h3 class="text-lg font-bold mb-2">
|
||||
项目制
|
||||
</h3>
|
||||
<p class="text-sm text-muted-foreground">
|
||||
按项目交付,固定价格或按阶段付款,适合明确需求的项目
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="page-surface p-6 text-center">
|
||||
<div class="w-12 h-12 bg-primary/20 rounded-xl flex items-center justify-center mx-auto mb-4">
|
||||
<Clock className="w-6 h-6 text-primary" />
|
||||
</div>
|
||||
<h3 class="text-lg font-bold mb-2">
|
||||
咨询/顾问
|
||||
</h3>
|
||||
<p class="text-sm text-muted-foreground">
|
||||
按小时咨询,帮助你做出技术决策,审查代码,指导团队
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Container>
|
||||
</section>
|
||||
|
||||
<!-- Delivery Style -->
|
||||
<section class="py-16 relative page-section-soft">
|
||||
<Container>
|
||||
<div class="page-content-main">
|
||||
<div class="flex items-center gap-4 mb-8">
|
||||
<div class="w-12 h-12 bg-yellow-500/20 rounded-xl flex items-center justify-center">
|
||||
<Target className="w-6 h-6 text-yellow-600 dark:text-yellow-400" />
|
||||
</div>
|
||||
<h2 class="text-3xl font-bold">
|
||||
工作方式
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<div class="page-surface p-8">
|
||||
<ul class="space-y-4">
|
||||
<li class="flex items-start gap-3">
|
||||
<Check className="w-6 h-6 text-green-500 shrink-0 mt-0.5" />
|
||||
<div>
|
||||
<strong class="block mb-1">敏捷迭代</strong>
|
||||
<p class="text-muted-foreground text-sm">
|
||||
2周一个迭代,快速交付可用功能,持续获取反馈
|
||||
</p>
|
||||
</div>
|
||||
</li>
|
||||
<li class="flex items-start gap-3">
|
||||
<Check className="w-6 h-6 text-green-500 shrink-0 mt-0.5" />
|
||||
<div>
|
||||
<strong class="block mb-1">透明沟通</strong>
|
||||
<p class="text-muted-foreground text-sm">
|
||||
定期同步进度,及时报告问题,保持信息对称
|
||||
</p>
|
||||
</div>
|
||||
</li>
|
||||
<li class="flex items-start gap-3">
|
||||
<Check className="w-6 h-6 text-green-500 shrink-0 mt-0.5" />
|
||||
<div>
|
||||
<strong class="block mb-1">代码质量</strong>
|
||||
<p class="text-muted-foreground text-sm">
|
||||
完整的测试覆盖,清晰的文档注释,可维护的代码结构
|
||||
</p>
|
||||
</div>
|
||||
</li>
|
||||
<li class="flex items-start gap-3">
|
||||
<Check className="w-6 h-6 text-green-500 shrink-0 mt-0.5" />
|
||||
<div>
|
||||
<strong class="block mb-1">产品思维</strong>
|
||||
<p class="text-muted-foreground text-sm">
|
||||
不只是写代码,还思考产品价值,用户体验和业务目标
|
||||
</p>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</Container>
|
||||
</section>
|
||||
|
||||
<!-- Availability -->
|
||||
<section class="py-16 relative">
|
||||
<Container>
|
||||
<div class="page-content-main">
|
||||
<div class="flex items-center gap-4 mb-8">
|
||||
<div class="w-12 h-12 bg-red-500/20 rounded-xl flex items-center justify-center">
|
||||
<Calendar className="w-6 h-6 text-red-500" />
|
||||
</div>
|
||||
<h2 class="text-3xl font-bold">
|
||||
当前可用性
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<div class="page-surface p-8">
|
||||
<div class="flex items-center gap-4 mb-6">
|
||||
<div class="w-4 h-4 bg-green-500 rounded-full animate-pulse"></div>
|
||||
<span class="text-lg font-semibold">
|
||||
目前可接受新项目
|
||||
</span>
|
||||
</div>
|
||||
<p class="text-muted-foreground mb-6">
|
||||
我目前可以接受 1-2 个新项目。偏好长期合作模式(技术合伙人或长期项目),但也会考虑短期项目。
|
||||
</p>
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<span class="px-3 py-1 bg-green-500/20 text-green-600 dark:text-green-400 rounded-full text-sm">
|
||||
可立即开始
|
||||
</span>
|
||||
<span class="px-3 py-1 bg-blue-500/20 text-blue-600 dark:text-blue-400 rounded-full text-sm">
|
||||
远程工作
|
||||
</span>
|
||||
<span class="px-3 py-1 bg-primary/20 text-primary dark:text-primary rounded-full text-sm">
|
||||
中英双语
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Container>
|
||||
</section>
|
||||
|
||||
<!-- Contact CTA -->
|
||||
<section class="py-16 relative page-section-soft">
|
||||
<Container>
|
||||
<div class="page-content-main text-center">
|
||||
<h2 class="text-3xl font-bold mb-6">
|
||||
准备好一起构建了吗?
|
||||
</h2>
|
||||
<p class="text-lg text-muted-foreground mb-8 page-content-narrow">
|
||||
告诉我你的项目想法,让我们看看能否合作。
|
||||
</p>
|
||||
<div class="flex flex-col sm:flex-row gap-4 justify-center">
|
||||
<a
|
||||
href="mailto:zhaoguiyang18@gmail.com"
|
||||
class="bg-primary hover:bg-primary/90 text-white px-8 py-3 rounded-lg font-semibold transition-colors inline-flex items-center justify-center gap-2"
|
||||
>
|
||||
<Mail className="w-5 h-5" />
|
||||
发送邮件
|
||||
</a>
|
||||
<a
|
||||
href="/zh/now"
|
||||
class="border border-primary text-primary hover:bg-primary hover:text-white px-8 py-3 rounded-lg font-semibold transition-colors inline-flex items-center justify-center gap-2"
|
||||
>
|
||||
<Radio className="w-5 h-5" />
|
||||
了解更多关于我
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</Container>
|
||||
</section>
|
||||
</main>
|
||||
<Footer lang={lang} client:only="react" />
|
||||
</Layout>
|
||||
|
||||
@@ -1,282 +1,109 @@
|
||||
---
|
||||
import Layout from "@/layouts/Layout.astro";
|
||||
import GlassHeader from "@/components/GlassHeader";
|
||||
import Footer from "@/components/Footer";
|
||||
import Container from "@/components/ui/Container.astro";
|
||||
import { useTranslations } from "@/i18n/utils";
|
||||
import type { Lang } from "@/types/i18n";
|
||||
import { defaultLang } from "@/i18n/ui";
|
||||
import { personalInfo, services, projects } from "@/lib/data/index";
|
||||
import Layout from '@/layouts/Layout.astro';
|
||||
import GlassHeader from '@/components/GlassHeader';
|
||||
import Footer from '@/components/Footer';
|
||||
import Container from '@/components/ui/Container.astro';
|
||||
import { personalInfo, projects } from '@/lib/data';
|
||||
import type { Lang } from '@/types/i18n';
|
||||
import { defaultLang } from '@/i18n/ui';
|
||||
import { sortPostsByDate } from '@/utils/blog-utils';
|
||||
|
||||
const lang = (Astro.currentLocale as Lang) || defaultLang;
|
||||
const t = useTranslations(lang);
|
||||
const pageTitle = t("site.title");
|
||||
const prefix = lang === "zh" ? "/zh" : "";
|
||||
const isZh = lang === 'zh';
|
||||
const prefix = isZh ? '/zh' : '';
|
||||
const featuredProjects = projects[lang].filter((item) => item.featured).slice(0, 3);
|
||||
|
||||
const localizedServices = services[lang];
|
||||
const featuredProjects = projects[lang].filter((project) => project.featured);
|
||||
const proofItems = isZh
|
||||
? ['8 年 Web 开发经验', '企业级复杂系统交付', '金融与区块链基础设施', '远程协作与稳定交付']
|
||||
: ['8 years in web engineering', 'Enterprise-grade complex systems', 'Finance & blockchain infrastructure', 'Remote-first stable delivery'];
|
||||
|
||||
const careerMilestones = [
|
||||
{
|
||||
period: t("home.career.card1.period"),
|
||||
title: t("home.career.card1.title"),
|
||||
outcome: t("home.career.card1.outcome"),
|
||||
},
|
||||
{
|
||||
period: t("home.career.card2.period"),
|
||||
title: t("home.career.card2.title"),
|
||||
outcome: t("home.career.card2.outcome"),
|
||||
},
|
||||
{
|
||||
period: t("home.career.card3.period"),
|
||||
title: t("home.career.card3.title"),
|
||||
outcome: t("home.career.card3.outcome"),
|
||||
},
|
||||
];
|
||||
const headline = isZh
|
||||
? '资深前端工程师,构建复杂系统与 AI 协作产品'
|
||||
: 'Senior Frontend Engineer building complex systems and AI-assisted products';
|
||||
|
||||
const keyFacts = [
|
||||
t("home.trust.item1"),
|
||||
t("home.trust.item3"),
|
||||
t("home.trust.item4"),
|
||||
];
|
||||
const intro = personalInfo.description[lang];
|
||||
|
||||
const postModules = await import.meta.glob('./blog/posts/*.md', { eager: true });
|
||||
const latestPosts = sortPostsByDate(
|
||||
Object.values(postModules).map((post: any) => ({
|
||||
title: post.frontmatter.title,
|
||||
description: post.frontmatter.description || '',
|
||||
slug: post.url?.split('/').filter(Boolean).pop() || '',
|
||||
date: post.frontmatter.date || post.frontmatter.pubDate || '',
|
||||
})),
|
||||
).slice(0, 3);
|
||||
---
|
||||
|
||||
<Layout title={pageTitle}>
|
||||
<GlassHeader client:load transition:persist="header" lang={lang} />
|
||||
<Layout title={isZh ? '首页' : 'Home'}>
|
||||
<GlassHeader lang={lang} client:load transition:persist="header" />
|
||||
|
||||
<main class="min-h-screen">
|
||||
<div class="relative overflow-hidden">
|
||||
<div class="absolute inset-0 -z-10">
|
||||
<div class="absolute left-1/2 top-0 h-[600px] w-[800px] -translate-x-1/2 -translate-y-1/2 bg-primary/10 blur-[120px]"></div>
|
||||
<div class="absolute right-0 top-1/4 h-[400px] w-[400px] bg-purple-500/5 blur-[100px]"></div>
|
||||
<div class="absolute left-0 top-1/2 h-[400px] w-[400px] bg-orange-500/5 blur-[100px]"></div>
|
||||
</div>
|
||||
<section class="relative flex min-h-[90vh] items-center py-24 lg:py-32">
|
||||
<Container className="relative z-10">
|
||||
<div class="mx-auto max-w-5xl space-y-16">
|
||||
<div class="space-y-8 text-center">
|
||||
<div class="space-y-4">
|
||||
<h1 class="text-5xl font-bold tracking-tight sm:text-7xl lg:text-8xl">
|
||||
<span class="bg-gradient-to-b from-foreground to-foreground/70 bg-clip-text text-transparent">{personalInfo.name}</span>
|
||||
</h1>
|
||||
<p class="text-2xl font-medium text-foreground/80 sm:text-3xl">{personalInfo.position[lang]}</p>
|
||||
</div>
|
||||
|
||||
<p class="mx-auto max-w-2xl text-lg leading-relaxed text-muted-foreground/90 sm:text-xl">{t("home.hero.summary")}</p>
|
||||
<main class="min-h-screen pt-24 pb-20">
|
||||
<Container>
|
||||
<section class="page-content-main space-y-8">
|
||||
<p class="text-sm font-semibold uppercase tracking-[0.2em] text-primary">{isZh ? 'ENGINEERING PROFILE' : 'ENGINEERING PROFILE'}</p>
|
||||
<h1 class="text-4xl font-bold tracking-tight text-foreground sm:text-5xl lg:text-6xl">{headline}</h1>
|
||||
<p class="max-w-3xl text-lg leading-relaxed text-muted-foreground">{intro}</p>
|
||||
|
||||
<div class="flex flex-wrap items-center justify-center gap-4">
|
||||
<a href={`${prefix}/about`} class="inline-flex h-12 items-center justify-center rounded-full bg-primary px-8 text-sm font-semibold text-primary-foreground shadow-lg shadow-primary/20 transition-all hover:translate-y-[-2px] hover:bg-primary/90 hover:shadow-xl hover:shadow-primary/30">
|
||||
{t("home.hero.ctaPrimary")}
|
||||
</a>
|
||||
<a href={`${prefix}/hire`} class="inline-flex h-12 items-center justify-center rounded-full border border-border bg-background/50 px-8 text-sm font-semibold text-foreground backdrop-blur-sm transition-all hover:translate-y-[-2px] hover:bg-muted">
|
||||
{t("home.hero.ctaSecondary")}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-wrap items-center justify-center gap-3 text-sm">
|
||||
{keyFacts.map((item) => (
|
||||
<span class="inline-flex items-center rounded-full border border-primary/10 bg-primary/5 px-4 py-1.5 font-medium text-primary/80 backdrop-blur-sm">
|
||||
<span class="mr-2 h-1 w-1 rounded-full bg-primary/50"></span>
|
||||
{item}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mx-auto max-w-3xl">
|
||||
<div class="group relative">
|
||||
<div class="absolute -inset-1 rounded-2xl bg-gradient-to-r from-primary/20 to-purple-500/20 opacity-50 blur transition duration-1000 group-hover:opacity-100 group-hover:duration-200"></div>
|
||||
<div class="page-surface relative overflow-hidden rounded-2xl border bg-card/40 backdrop-blur-md">
|
||||
<div class="flex items-center justify-between border-b border-border/50 bg-muted/20 px-5 py-3">
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="h-3 w-3 rounded-full bg-red-400/80 shadow-[0_0_8px_rgba(248,113,113,0.4)]"></span>
|
||||
<span class="h-3 w-3 rounded-full bg-yellow-400/80 shadow-[0_0_8px_rgba(250,204,21,0.4)]"></span>
|
||||
<span class="h-3 w-3 rounded-full bg-green-400/80 shadow-[0_0_8px_rgba(74,222,128,0.4)]"></span>
|
||||
</div>
|
||||
<span class="font-mono text-xs font-medium text-muted-foreground/70 tracking-tight">{personalInfo.terminal.username}</span>
|
||||
</div>
|
||||
<div class="space-y-3 p-6 font-mono text-sm leading-relaxed text-foreground/90">
|
||||
<div class="flex gap-3">
|
||||
<span class="text-primary font-bold">~</span>
|
||||
<p><span class="text-primary/70">whoami</span> {personalInfo.name}</p>
|
||||
</div>
|
||||
<div class="flex gap-3">
|
||||
<span class="text-primary font-bold">~</span>
|
||||
<p><span class="text-primary/70">role</span> {personalInfo.position[lang]}</p>
|
||||
</div>
|
||||
<div class="flex gap-3">
|
||||
<span class="text-primary font-bold">~</span>
|
||||
<p><span class="text-primary/70">stack</span> TypeScript, React, Node.js, Astro</p>
|
||||
</div>
|
||||
<div class="flex gap-3">
|
||||
<span class="text-primary font-bold">~</span>
|
||||
<p><span class="text-primary/70">status</span> {t("home.hero.terminalStatus")}</p>
|
||||
</div>
|
||||
<div class="flex gap-3">
|
||||
<span class="text-primary font-bold">~</span>
|
||||
<p><span class="text-primary/70">contact</span> {personalInfo.github}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-wrap gap-4 pt-2">
|
||||
<a href={`${prefix}/contact`} class="inline-flex h-11 items-center rounded-lg bg-primary px-6 text-sm font-semibold text-primary-foreground hover:bg-primary/90">{isZh ? '远程岗位沟通' : 'Hire for Remote Role'}</a>
|
||||
<a href={`${prefix}/contact#project-collaboration`} class="inline-flex h-11 items-center rounded-lg border border-border px-6 text-sm font-semibold text-foreground hover:bg-muted">{isZh ? '发起项目合作' : 'Start a Project'}</a>
|
||||
</div>
|
||||
</Container>
|
||||
</section>
|
||||
|
||||
<div class="space-y-40 pb-40">
|
||||
<section class="relative">
|
||||
<div class="py-24 lg:py-32">
|
||||
<Container>
|
||||
<div class="mb-16 flex flex-col gap-6 md:flex-row md:items-end md:justify-between">
|
||||
<div class="space-y-4">
|
||||
<h2 class="text-4xl font-bold tracking-tight sm:text-5xl lg:text-6xl">{t("home.services.title")}</h2>
|
||||
<p class="max-w-2xl text-lg leading-relaxed text-muted-foreground/90">{t("home.services.description")}</p>
|
||||
</div>
|
||||
<a href={`${prefix}/services`} class="inline-flex items-center text-sm font-bold text-primary transition-colors hover:text-primary/70">
|
||||
{t("services.viewAll")}
|
||||
<svg class="ml-1 h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 8l4 4m0 0l-4 4m4-4H3"/></svg>
|
||||
</a>
|
||||
</div>
|
||||
<ul class="grid gap-3 sm:grid-cols-2 pt-4">
|
||||
{proofItems.map((item) => (
|
||||
<li class="page-surface p-4 text-sm font-medium text-foreground/90">{item}</li>
|
||||
))}
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<div class="grid gap-10 md:grid-cols-2">
|
||||
{localizedServices.map((service) => (
|
||||
<article class="group relative rounded-3xl p-6 transition-all duration-500 hover:bg-card/50 hover:shadow-xl hover:shadow-primary/5">
|
||||
<div class="mb-8 flex items-center gap-6">
|
||||
<div class={`flex h-14 w-14 items-center justify-center rounded-2xl bg-gradient-to-br ${service.icon.gradient} text-white shadow-lg transition-transform duration-500 group-hover:scale-110 group-hover:rotate-3`}>
|
||||
<svg class="h-6 w-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<Fragment set:html={service.icon.svg} />
|
||||
</svg>
|
||||
</div>
|
||||
<h3 class="text-2xl font-bold tracking-tight">{service.title}</h3>
|
||||
</div>
|
||||
<ul class="space-y-4">
|
||||
{service.items.slice(0, 3).map((item) => (
|
||||
<li class="flex items-start gap-3 text-muted-foreground group-hover:text-foreground/90 transition-colors">
|
||||
<span class="mt-2.5 h-1 w-1 shrink-0 rounded-full bg-primary/30 group-hover:bg-primary transition-all"></span>
|
||||
<span class="text-base leading-relaxed">{item}</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</article>
|
||||
))}
|
||||
</div>
|
||||
</Container>
|
||||
<section class="page-content-main mt-20">
|
||||
<div class="mb-8 flex items-end justify-between gap-4">
|
||||
<h2 class="text-2xl font-bold tracking-tight sm:text-3xl">{isZh ? '精选工程案例' : 'Selected Engineering Cases'}</h2>
|
||||
<a href={`${prefix}/projects`} class="text-sm font-semibold text-primary hover:text-primary/80">{isZh ? '查看全部案例' : 'View All Cases'}</a>
|
||||
</div>
|
||||
|
||||
<div class="grid gap-6 lg:grid-cols-3">
|
||||
{featuredProjects.map((project) => (
|
||||
<article class="page-surface p-6">
|
||||
<p class="text-xs font-semibold uppercase tracking-wider text-primary/80">{project.systemType}</p>
|
||||
<h3 class="mt-2 text-lg font-bold">{project.title}</h3>
|
||||
<p class="mt-3 text-sm leading-relaxed text-muted-foreground">{project.context}</p>
|
||||
<p class="mt-4 text-xs font-semibold uppercase tracking-wide text-foreground/70">{isZh ? '结果' : 'Outcome'}</p>
|
||||
<p class="mt-1 text-sm text-foreground/90">{project.outcomes?.[0] ?? project.impact}</p>
|
||||
</article>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="relative">
|
||||
<Container>
|
||||
<div class="mb-16 space-y-3">
|
||||
<h2 class="text-4xl font-bold tracking-tight sm:text-5xl">{t("home.career.title")}</h2>
|
||||
<p class="max-w-xl text-lg text-muted-foreground">{t("home.career.description")}</p>
|
||||
</div>
|
||||
<section class="page-content-main mt-20">
|
||||
<div class="mb-8 flex items-end justify-between gap-4">
|
||||
<h2 class="text-2xl font-bold tracking-tight sm:text-3xl">{isZh ? '最新写作' : 'Latest Writing'}</h2>
|
||||
<a href={`${prefix}/blog`} class="text-sm font-semibold text-primary hover:text-primary/80">{isZh ? '查看全部文章' : 'View All Writing'}</a>
|
||||
</div>
|
||||
|
||||
<div class="relative mx-auto max-w-4xl">
|
||||
<div class="absolute left-0 top-0 h-full w-px bg-gradient-to-b from-primary/50 via-primary/10 to-transparent sm:left-1/2"></div>
|
||||
|
||||
<div class="space-y-12">
|
||||
{careerMilestones.map((item, index) => (
|
||||
<div class={`relative flex flex-col sm:flex-row ${index % 2 === 0 ? 'sm:flex-row-reverse' : ''}`}>
|
||||
<div class="absolute -left-1.5 top-2 h-3 w-3 rounded-full border-2 border-primary bg-background sm:left-1/2 sm:-ml-1.5"></div>
|
||||
<div class="w-full pl-8 sm:w-1/2 sm:px-12">
|
||||
<article class="rounded-2xl p-4 transition-all hover:bg-card/40">
|
||||
<span class="text-xs font-bold uppercase tracking-wider text-primary/60">{item.period}</span>
|
||||
<h3 class="mt-2 text-xl font-bold leading-tight">{item.title}</h3>
|
||||
<p class="mt-3 text-sm leading-relaxed text-muted-foreground/90">{item.outcome}</p>
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-16 text-center">
|
||||
<a href={`${prefix}/about`} class="inline-flex h-12 items-center rounded-full border border-border bg-background px-8 text-sm font-semibold transition-all hover:bg-muted">
|
||||
{t("home.career.cta")}
|
||||
</a>
|
||||
</div>
|
||||
</Container>
|
||||
</section>
|
||||
|
||||
<section class="relative">
|
||||
<div class="py-24 lg:py-32">
|
||||
<Container>
|
||||
<div class="mb-12 space-y-3">
|
||||
<h2 class="text-4xl font-bold tracking-tight sm:text-5xl">{t("home.featured.title")}</h2>
|
||||
</div>
|
||||
|
||||
<div class="grid gap-8 md:grid-cols-2 lg:grid-cols-3">
|
||||
{featuredProjects.map((project) => (
|
||||
<article class="group flex flex-col overflow-hidden rounded-3xl border border-border/50 bg-card/50 backdrop-blur-sm transition-all duration-500 hover:-translate-y-2 hover:shadow-2xl hover:shadow-primary/5">
|
||||
{/* 顶部封面图区域 */}
|
||||
<div class="relative aspect-video overflow-hidden">
|
||||
<div class={`absolute inset-0 bg-gradient-to-br ${project.image?.bg || 'from-primary/20 to-purple-500/20'} transition-transform duration-700 group-hover:scale-110`}></div>
|
||||
<div class="absolute inset-0 flex items-center justify-center">
|
||||
<span class="text-6xl transition-transform duration-500 group-hover:scale-110 group-hover:rotate-3">{project.icon}</span>
|
||||
</div>
|
||||
<div class="absolute top-4 right-4">
|
||||
<span class="rounded-full bg-background/80 px-3 py-1 text-[10px] font-bold uppercase tracking-wider text-foreground backdrop-blur-md">
|
||||
{project.type}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 内容区域 */}
|
||||
<div class="flex flex-1 flex-col p-6 lg:p-8">
|
||||
<div class="flex-1 space-y-4">
|
||||
<h3 class="text-2xl font-bold tracking-tight">{project.title}</h3>
|
||||
<p class="line-clamp-2 text-sm leading-relaxed text-muted-foreground">
|
||||
{project.impact}
|
||||
</p>
|
||||
|
||||
<div class="flex flex-wrap gap-2">
|
||||
{project.tech.map((tech) => (
|
||||
<span class="rounded-md bg-primary/5 px-2 py-1 text-[10px] font-medium text-primary/80">
|
||||
{tech}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 操作按钮 */}
|
||||
<div class="mt-8 flex items-center justify-between gap-4">
|
||||
<a href={`${prefix}/projects`} class="inline-flex h-10 items-center justify-center rounded-full bg-primary px-6 text-xs font-bold text-primary-foreground transition-all hover:bg-primary/90">
|
||||
{t("home.featured.ctaPrimary")}
|
||||
</a>
|
||||
{project.links?.github && (
|
||||
<a href={project.links.github} target="_blank" rel="noopener noreferrer" class="inline-flex h-10 w-10 items-center justify-center rounded-full border border-border bg-background/50 text-muted-foreground transition-all hover:bg-muted hover:text-foreground">
|
||||
<svg class="h-5 w-5" fill="currentColor" viewBox="0 0 24 24"><path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 4.125 9.642 9.807 11.53 0.6.111 0.799-0.26 0.799-0.577 0-0.285-0.011-1.04-0.017-2.042-3.338 0.727-4.042-1.61-4.042-1.61-0.546-1.387-1.333-1.756-1.333-1.756-1.089-0.745 0.083-0.729 0.083-0.729 1.205 0.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492 0.997 0.107-0.775 0.418-1.305 0.762-1.604-2.665-0.305-5.467-1.334-5.467-5.931 0-1.311 0.469-2.381 1.236-3.221-0.124-0.303-0.535-1.524 0.117-3.176 0 0 1.008-0.322 3.301 1.23 0.96-0.267 1.98-0.399 3-0.405 1.02 0.006 2.04 0.138 3 0.405 2.28-1.552 3.285-1.23 3.285-1.23 0.654 1.653 0.242 2.874 0.118 3.176 0.77 0.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921 0.43 0.372 0.823 1.102 0.823 2.222 0 1.606-0.014 2.898-0.014 3.293 0 0.319 0.192 0.694 0.801 0.576 5.687-1.889 9.812-6.228 9.812-11.53 0-6.627-5.373-12-12-12z"/></svg>
|
||||
</a>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
))}
|
||||
</div>
|
||||
</Container>
|
||||
<div class="grid gap-4 md:grid-cols-3">
|
||||
{latestPosts.map((post) => (
|
||||
<article class="page-surface p-5">
|
||||
<p class="text-xs font-semibold uppercase tracking-wider text-primary/80">{post.date}</p>
|
||||
<h3 class="mt-2 text-base font-bold leading-snug">
|
||||
<a href={`${prefix}/blog/posts/${post.slug}`} class="hover:text-primary">{post.title}</a>
|
||||
</h3>
|
||||
<p class="mt-2 line-clamp-3 text-sm text-muted-foreground">{post.description}</p>
|
||||
</article>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="relative">
|
||||
<Container>
|
||||
<div class="flex flex-col items-center text-center">
|
||||
<h2 class="text-4xl font-bold tracking-tight sm:text-5xl lg:text-6xl mb-6">{t("home.final.title")}</h2>
|
||||
<p class="text-lg leading-relaxed text-muted-foreground/90 sm:text-xl max-w-2xl mb-10">{t("home.final.description")}</p>
|
||||
<div class="flex flex-wrap justify-center gap-4">
|
||||
<a href={`${prefix}/about`} class="inline-flex h-12 items-center justify-center rounded-full bg-primary px-8 text-sm font-semibold text-primary-foreground transition-all hover:translate-y-[-2px] hover:bg-primary/90">
|
||||
{t("home.final.ctaPrimary")}
|
||||
</a>
|
||||
<a href={`${prefix}/hire`} class="inline-flex h-12 items-center justify-center rounded-full border border-border px-8 text-sm font-semibold text-foreground transition-all hover:translate-y-[-2px] hover:bg-muted">
|
||||
{t("home.final.ctaSecondary")}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</Container>
|
||||
<section class="page-content-main mt-20 page-surface p-8">
|
||||
<h2 class="text-2xl font-bold tracking-tight">{isZh ? '下一步' : 'Next Step'}</h2>
|
||||
<p class="mt-3 text-muted-foreground">{isZh ? '如果你正在招聘远程工程师,或希望推进复杂系统项目,我很乐意沟通。' : 'If you are hiring for a remote engineering role or need help shipping a complex system, I would love to connect.'}</p>
|
||||
<div class="mt-6 flex flex-wrap gap-3">
|
||||
<a href={`${prefix}/contact`} class="inline-flex h-10 items-center rounded-lg bg-primary px-5 text-sm font-semibold text-primary-foreground">{isZh ? '联系我' : 'Contact Me'}</a>
|
||||
<a href={`${prefix}/about`} class="inline-flex h-10 items-center rounded-lg border border-border px-5 text-sm font-semibold hover:bg-muted">{isZh ? '了解更多' : 'About Me'}</a>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</Container>
|
||||
</main>
|
||||
|
||||
<Footer lang={lang} client:only="react" />
|
||||
<Footer lang={lang} client:load />
|
||||
</Layout>
|
||||
|
||||
@@ -1,228 +1,67 @@
|
||||
---
|
||||
import Layout from "@/layouts/Layout.astro";
|
||||
import GlassHeader from "@/components/GlassHeader";
|
||||
import Footer from "@/components/Footer";
|
||||
import Container from "@/components/ui/Container.astro";
|
||||
import { useTranslations } from "@/i18n/utils";
|
||||
import { Hammer, Zap, Telescope, Bot, Target, Rocket, Sparkles, MessageSquare, Handshake, MapPin } from "lucide-react";
|
||||
import type { Lang } from "@/types/i18n";
|
||||
import { defaultLang } from "@/i18n/ui";
|
||||
import Layout from '@/layouts/Layout.astro';
|
||||
import GlassHeader from '@/components/GlassHeader';
|
||||
import Footer from '@/components/Footer';
|
||||
import Container from '@/components/ui/Container.astro';
|
||||
import type { Lang } from '@/types/i18n';
|
||||
import { defaultLang } from '@/i18n/ui';
|
||||
|
||||
const lang = Astro.currentLocale as Lang || defaultLang;
|
||||
const t = useTranslations(lang);
|
||||
const pageTitle = lang === 'zh' ? '现在' : 'Now';
|
||||
const lang = (Astro.currentLocale as Lang) || defaultLang;
|
||||
const isZh = lang === 'zh';
|
||||
|
||||
const doing = isZh
|
||||
? ['推进工程化案例化的个人站升级', '优化远程协作下的交付流程与文档标准', '持续打磨 AI 协作开发实践']
|
||||
: ['Upgrading this portfolio into an engineering case-study site', 'Improving remote-first delivery workflow and documentation quality', 'Refining practical AI-assisted development workflows'];
|
||||
|
||||
const exploring = isZh
|
||||
? ['复杂权限系统的前端边界设计', '大规模业务表单与状态流管理', '更稳定的跨团队异步协作机制']
|
||||
: ['Frontend boundary design for complex permission systems', 'Scalable state management for large business forms', 'More stable async collaboration practices across teams'];
|
||||
|
||||
const shipping = isZh
|
||||
? ['工程案例库结构重构', '导航与路由转化路径优化', '双语文案统一与信息层级简化']
|
||||
: ['Refactoring project pages into engineering case studies', 'Optimizing navigation and conversion routes', 'Unifying EN/ZH copy and simplifying information hierarchy'];
|
||||
---
|
||||
|
||||
<Layout title={pageTitle}>
|
||||
<Layout title={isZh ? '现在' : 'Now'}>
|
||||
<GlassHeader lang={lang} client:load transition:persist="header" />
|
||||
<main class="min-h-screen relative overflow-hidden">
|
||||
<!-- 背景装饰 -->
|
||||
<div class="absolute inset-0 -z-10">
|
||||
<div class="absolute left-1/2 top-0 h-[800px] w-[1000px] -translate-x-1/2 -translate-y-1/2 bg-primary/10 blur-[120px]"></div>
|
||||
<div class="absolute right-0 top-1/3 h-[500px] w-[500px] bg-blue-500/5 blur-[100px]"></div>
|
||||
</div>
|
||||
|
||||
<!-- Now Page Hero -->
|
||||
<section class="pt-32 pb-24 relative overflow-hidden">
|
||||
<div class="page-hero-overlay" aria-hidden="true"></div>
|
||||
<main class="min-h-screen pt-24 pb-20">
|
||||
<Container>
|
||||
<section class="page-content-main">
|
||||
<h1 class="text-4xl font-bold tracking-tight sm:text-5xl">{isZh ? '现在' : 'Now'}</h1>
|
||||
<p class="mt-4 text-lg text-muted-foreground">{isZh ? '我当前的工作重点与近期交付。' : 'What I am focusing on right now and what I am shipping recently.'}</p>
|
||||
</section>
|
||||
|
||||
<Container className="relative z-10">
|
||||
<div class="max-w-4xl mx-auto text-center">
|
||||
<div class="inline-flex items-center gap-2 px-3 py-1 rounded-full bg-primary/10 border border-primary/20 text-primary text-xs font-bold uppercase tracking-wider mb-6 animate-fade-in">
|
||||
<span class="relative flex h-2 w-2">
|
||||
<span class="animate-ping absolute inline-flex h-full w-full rounded-full bg-primary opacity-75"></span>
|
||||
<span class="relative inline-flex rounded-full h-2 w-2 bg-primary"></span>
|
||||
</span>
|
||||
{lang === 'zh' ? '实时状态' : "Live Status"}
|
||||
</div>
|
||||
<h1 class="page-title-gradient text-6xl md:text-8xl font-bold mb-8 tracking-tight">
|
||||
{lang === 'zh' ? '现在' : 'Now'}
|
||||
</h1>
|
||||
<p class="text-xl md:text-2xl text-muted-foreground leading-relaxed max-w-2xl mx-auto">
|
||||
{lang === 'zh'
|
||||
? '关于我现在正在关注的事、正在构建的产品以及我的生活状态。'
|
||||
: 'What I am currently focusing on, the products I am building, and my life status.'}
|
||||
</p>
|
||||
</div>
|
||||
</Container>
|
||||
</section>
|
||||
<section class="page-content-main mt-10 grid gap-6 md:grid-cols-3">
|
||||
<article class="page-surface p-6">
|
||||
<h2 class="text-lg font-bold">{isZh ? '在做什么' : 'Doing'}</h2>
|
||||
<ul class="mt-4 space-y-2 text-sm text-muted-foreground">
|
||||
{doing.map((item) => <li>• {item}</li>)}
|
||||
</ul>
|
||||
</article>
|
||||
|
||||
<!-- Main Content -->
|
||||
<section class="pb-32 relative mt-12 md:mt-24">
|
||||
<Container>
|
||||
<div class="max-w-4xl mx-auto space-y-24">
|
||||
<!-- Building Section -->
|
||||
<div class="space-y-10">
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="w-12 h-12 bg-primary/10 rounded-xl flex items-center justify-center border border-primary/20">
|
||||
<Hammer className="w-6 h-6 text-primary" />
|
||||
</div>
|
||||
<h2 class="text-3xl md:text-4xl font-bold tracking-tight">
|
||||
{lang === 'zh' ? '正在构建' : 'Building'}
|
||||
</h2>
|
||||
</div>
|
||||
<article class="page-surface p-6">
|
||||
<h2 class="text-lg font-bold">{isZh ? '在研究什么' : 'Exploring'}</h2>
|
||||
<ul class="mt-4 space-y-2 text-sm text-muted-foreground">
|
||||
{exploring.map((item) => <li>• {item}</li>)}
|
||||
</ul>
|
||||
</article>
|
||||
|
||||
<div class="group relative">
|
||||
<div class="absolute -inset-1 rounded-[2rem] bg-gradient-to-r from-primary/30 to-blue-600/30 opacity-20 blur-xl transition duration-500 group-hover:opacity-40"></div>
|
||||
<div class="page-surface relative p-8 md:p-12 overflow-hidden border-primary/10 rounded-[2rem]">
|
||||
<div class="flex flex-col md:flex-row gap-10 items-start">
|
||||
<div class="w-24 h-24 bg-gradient-to-br from-primary to-blue-600 rounded-2xl flex items-center justify-center flex-shrink-0 shadow-lg shadow-primary/20 group-hover:scale-105 transition-transform duration-500">
|
||||
<Zap className="w-12 h-12 text-white" />
|
||||
</div>
|
||||
<div class="flex-1 space-y-6">
|
||||
<div class="flex items-center justify-between">
|
||||
<h3 class="text-3xl font-bold">Elynd</h3>
|
||||
<span class="px-4 py-1.5 bg-green-500/10 text-green-600 dark:text-green-400 rounded-full text-xs font-bold border border-green-500/20 uppercase tracking-wider">Active</span>
|
||||
</div>
|
||||
<p class="text-xl text-muted-foreground leading-relaxed">
|
||||
{lang === 'zh'
|
||||
? '一个开放的 AI 工作空间,让构建者能够更智能地工作。从 AI 第一性原理出发,打造完全开放、可自托管的协作工具。'
|
||||
: 'An open AI workspace for builders to work smarter. Built from first principles of AI, creating fully open, self-hostable collaboration tools.'}
|
||||
</p>
|
||||
<div class="flex flex-wrap gap-3 pt-2">
|
||||
{['AI Product', 'TypeScript', 'Open Source', 'Astro'].map(tag => (
|
||||
<span class="px-4 py-2 bg-secondary/50 text-secondary-foreground rounded-lg text-sm font-medium border border-border/50">{tag}</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<article class="page-surface p-6">
|
||||
<h2 class="text-lg font-bold">{isZh ? '最近交付' : 'Shipping'}</h2>
|
||||
<ul class="mt-4 space-y-2 text-sm text-muted-foreground">
|
||||
{shipping.map((item) => <li>• {item}</li>)}
|
||||
</ul>
|
||||
</article>
|
||||
</section>
|
||||
|
||||
<!-- Exploring Section -->
|
||||
<div class="space-y-10">
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="w-12 h-12 bg-blue-500/10 rounded-xl flex items-center justify-center border border-blue-500/20">
|
||||
<Telescope className="w-6 h-6 text-blue-500" />
|
||||
</div>
|
||||
<h2 class="text-3xl md:text-4xl font-bold tracking-tight">
|
||||
{lang === 'zh' ? '正在探索' : 'Exploring'}
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<div class="grid gap-8 md:grid-cols-2">
|
||||
{[
|
||||
{
|
||||
title: lang === 'zh' ? 'AI Agents 与工作流自动化' : 'AI Agents & Workflow Automation',
|
||||
desc: lang === 'zh' ? '研究如何构建能够自主完成复杂任务的 AI Agent,以及如何将 AI 能力融入日常工作流。' : 'Researching how to build AI Agents that can autonomously complete complex tasks, and how to integrate AI capabilities into daily workflows.',
|
||||
icon: Bot
|
||||
},
|
||||
{
|
||||
title: lang === 'zh' ? '产品导向的开发实践' : 'Product-Led Development Practices',
|
||||
desc: lang === 'zh' ? '探索从产品角度思考开发,构建真正解决用户问题的产品,而不仅仅是技术实现。' : 'Exploring product thinking in development, building products that truly solve user problems, not just technical implementations.',
|
||||
icon: Target
|
||||
}
|
||||
].map(item => (
|
||||
<div class="page-surface p-8 rounded-3xl hover:translate-y-[-4px] transition-all duration-300 border-border/50 group">
|
||||
<div class="w-12 h-12 bg-muted rounded-xl flex items-center justify-center mb-6 group-hover:scale-110 transition-transform duration-300">
|
||||
<item.icon className="w-6 h-6 text-foreground" />
|
||||
</div>
|
||||
<h3 class="text-2xl font-bold mb-4">{item.title}</h3>
|
||||
<p class="text-muted-foreground text-base leading-relaxed">{item.desc}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Shipping Section -->
|
||||
<div class="space-y-10">
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="w-12 h-12 bg-green-500/10 rounded-xl flex items-center justify-center border border-green-500/20">
|
||||
<Rocket className="w-6 h-6 text-green-500" />
|
||||
</div>
|
||||
<h2 class="text-3xl md:text-4xl font-bold tracking-tight">
|
||||
{lang === 'zh' ? '最近发布' : 'Shipping'}
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<div class="space-y-6">
|
||||
{[
|
||||
{
|
||||
title: lang === 'zh' ? '个人网站全新改版' : 'Portfolio Redesign',
|
||||
desc: lang === 'zh' ? '基于 Astro 5 和 Tailwind 4 的高性能双语站点。' : 'High-performance bilingual site built with Astro 5 & Tailwind 4.',
|
||||
icon: Sparkles,
|
||||
color: 'bg-green-500/10',
|
||||
iconColor: 'text-green-500'
|
||||
},
|
||||
{
|
||||
title: lang === 'zh' ? 'DeepSeek 满血版接入' : 'DeepSeek Full Access',
|
||||
desc: lang === 'zh' ? '在本地工作流中深度整合 DeepSeek-V3 提升效率。' : 'Deep integration of DeepSeek-V3 in local workflows for efficiency.',
|
||||
icon: MessageSquare,
|
||||
color: 'bg-blue-500/10',
|
||||
iconColor: 'text-blue-500'
|
||||
}
|
||||
].map(item => (
|
||||
<div class="group relative flex items-center gap-6 p-4 rounded-2xl hover:bg-muted/50 transition-all duration-300">
|
||||
<div class={`w-12 h-12 ${item.color} rounded-xl flex items-center justify-center flex-shrink-0 group-hover:scale-110 transition-transform`}>
|
||||
<item.icon className={`w-6 h-6 ${item.iconColor}`} />
|
||||
</div>
|
||||
<div class="flex-1 min-w-0">
|
||||
<h3 class="font-bold text-lg group-hover:text-primary transition-colors truncate">
|
||||
{item.title}
|
||||
</h3>
|
||||
<p class="text-sm text-muted-foreground line-clamp-1">
|
||||
{item.desc}
|
||||
</p>
|
||||
</div>
|
||||
<div class="text-muted-foreground/30 group-hover:text-primary/50 transition-colors">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12h14"/><path d="m12 5 7 7-7 7"/></svg>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Bottom Grid: Collaboration & Meta -->
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-8 pt-8">
|
||||
<!-- Collaboration Card -->
|
||||
<div class="relative group h-full">
|
||||
<div class="absolute -inset-1 rounded-[2.5rem] bg-gradient-to-br from-yellow-500/20 to-orange-500/20 blur opacity-75"></div>
|
||||
<div class="page-surface relative p-10 border-yellow-500/10 bg-gradient-to-br from-card to-yellow-500/5 rounded-[2.5rem] h-full flex flex-col justify-between">
|
||||
<div>
|
||||
<div class="w-14 h-14 bg-yellow-500/10 rounded-2xl flex items-center justify-center mb-8">
|
||||
<Handshake className="w-8 h-8 text-yellow-600 dark:text-yellow-400" />
|
||||
</div>
|
||||
<h3 class="text-3xl font-bold mb-4">{lang === 'zh' ? '开放合作' : 'Collaborate'}</h3>
|
||||
<p class="text-lg text-muted-foreground mb-8 leading-relaxed">
|
||||
{lang === 'zh'
|
||||
? '始终对有趣的产品想法 and 技术咨询持开放态度。'
|
||||
: "Always open to interesting product ideas and technical consulting."}
|
||||
</p>
|
||||
</div>
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||
<a href={lang === 'zh' ? '/zh/hire' : '/hire'} class="flex items-center justify-center py-4 rounded-2xl bg-primary text-primary-foreground font-bold text-base hover:scale-[1.02] transition-transform">
|
||||
{lang === 'zh' ? '合作方式' : 'View Options'}
|
||||
</a>
|
||||
<a href="mailto:zhaoguiyang18@gmail.com" class="flex items-center justify-center py-4 rounded-2xl border border-border bg-background/50 text-foreground font-bold text-base hover:bg-muted transition-colors">
|
||||
{lang === 'zh' ? '发送邮件' : 'Email Me'}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Meta/Status Card -->
|
||||
<div class="page-surface p-10 border-border/50 rounded-[2.5rem] flex flex-col items-center justify-center text-center space-y-6">
|
||||
<div class="w-20 h-20 bg-muted rounded-full flex items-center justify-center text-3xl">
|
||||
<MapPin className="w-10 h-10 text-primary" />
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<h3 class="text-2xl font-bold">{lang === 'zh' ? '当前位置' : 'Location'}</h3>
|
||||
<p class="text-muted-foreground">{lang === 'zh' ? '中国 · 杭州' : 'Hangzhou, China'}</p>
|
||||
</div>
|
||||
<div class="pt-4 w-full">
|
||||
<div class="inline-flex items-center gap-3 px-5 py-2.5 rounded-full bg-muted/50 border border-border/50 mx-auto">
|
||||
<span class="text-xs text-muted-foreground uppercase font-bold tracking-widest border-r border-border/50 pr-3">
|
||||
{lang === 'zh' ? '最后更新' : 'Updated'}
|
||||
</span>
|
||||
<span class="text-sm font-semibold">2025.03.14</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Container>
|
||||
</section>
|
||||
<section class="page-content-main mt-8 page-surface p-6">
|
||||
<p class="text-sm text-muted-foreground">
|
||||
{isZh ? '最后更新:2026-03-16' : 'Last updated: 2026-03-16'}
|
||||
</p>
|
||||
</section>
|
||||
</Container>
|
||||
</main>
|
||||
<Footer lang={lang} client:only="react" />
|
||||
|
||||
<Footer lang={lang} client:load />
|
||||
</Layout>
|
||||
|
||||
@@ -1,114 +1,133 @@
|
||||
---
|
||||
import Layout from "@/layouts/Layout.astro";
|
||||
import GlassHeader from "@/components/GlassHeader";
|
||||
import Footer from "@/components/Footer";
|
||||
import Container from "@/components/ui/Container.astro";
|
||||
import ProjectCard from "@/components/ProjectCard.astro";
|
||||
import { useTranslations } from "@/i18n/utils";
|
||||
import type { Lang } from "@/types/i18n";
|
||||
import { defaultLang } from "@/i18n/ui";
|
||||
import { projects } from "@/lib/data/index";
|
||||
import Layout from '@/layouts/Layout.astro';
|
||||
import GlassHeader from '@/components/GlassHeader';
|
||||
import Footer from '@/components/Footer';
|
||||
import Container from '@/components/ui/Container.astro';
|
||||
import { projects } from '@/lib/data';
|
||||
import type { Lang } from '@/types/i18n';
|
||||
import { defaultLang } from '@/i18n/ui';
|
||||
|
||||
// 使用Astro.currentLocale获取当前语言环境
|
||||
const lang = Astro.currentLocale as Lang || defaultLang;
|
||||
const t = useTranslations(lang);
|
||||
const pageTitle = t('projects.title');
|
||||
const lang = (Astro.currentLocale as Lang) || defaultLang;
|
||||
const isZh = lang === 'zh';
|
||||
const pageProjects = projects[lang];
|
||||
|
||||
// 根据当前语言获取项目数据
|
||||
const currentProjects = projects[lang as keyof typeof projects] || projects.en;
|
||||
|
||||
const filterOptions = [
|
||||
{ key: "all", label: t("project.filter.all") },
|
||||
{ key: "product", label: t("project.type.product") },
|
||||
{ key: "client", label: t("project.type.client") },
|
||||
{ key: "experiment", label: t("project.type.experiment") },
|
||||
const filters = [
|
||||
{ key: 'all', label: isZh ? '全部' : 'All' },
|
||||
{ key: 'product', label: isZh ? '产品系统' : 'Product Systems' },
|
||||
{ key: 'client', label: isZh ? '企业项目' : 'Client Systems' },
|
||||
{ key: 'experiment', label: isZh ? '实验项目' : 'Experiments' },
|
||||
];
|
||||
---
|
||||
|
||||
<Layout title={pageTitle}>
|
||||
<Layout title={isZh ? '项目' : 'Projects'}>
|
||||
<GlassHeader lang={lang} client:load transition:persist="header" />
|
||||
<main class="min-h-screen relative overflow-hidden">
|
||||
<div
|
||||
aria-hidden="true"
|
||||
class="page-hero-overlay"
|
||||
></div>
|
||||
|
||||
<section class="relative z-10 py-24">
|
||||
<Container>
|
||||
<div class="text-center mb-8">
|
||||
<h1 class="page-title-gradient text-5xl md:text-6xl font-bold mb-6">
|
||||
{t('projects.title')}
|
||||
</h1>
|
||||
<p class="text-lg text-muted-foreground page-content-main mb-12 leading-relaxed">
|
||||
{t('projects.slogan')} {t('projects.description')}
|
||||
</p>
|
||||
<main class="min-h-screen pt-24 pb-20" data-projects-root>
|
||||
<Container>
|
||||
<section class="page-content-main">
|
||||
<h1 class="text-4xl font-bold tracking-tight sm:text-5xl">{isZh ? '工程案例' : 'Engineering Case Studies'}</h1>
|
||||
<p class="mt-4 text-lg leading-relaxed text-muted-foreground">
|
||||
{isZh
|
||||
? '围绕复杂系统建设的真实项目经验,重点展示技术挑战、职责边界和结果。'
|
||||
: 'Real projects focused on complex system delivery, highlighting technical challenges, responsibilities, and outcomes.'}
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section class="page-content-main mt-8">
|
||||
<div class="page-surface mb-8 flex flex-wrap gap-2 p-2">
|
||||
{filters.map((filter, index) => (
|
||||
<button
|
||||
type="button"
|
||||
data-filter={filter.key}
|
||||
aria-pressed={index === 0 ? 'true' : 'false'}
|
||||
class={`rounded-md px-4 py-2 text-sm font-semibold ${index === 0 ? 'bg-primary text-primary-foreground' : 'text-muted-foreground hover:bg-muted hover:text-foreground'}`}
|
||||
>
|
||||
{filter.label}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</Container>
|
||||
</section>
|
||||
|
||||
<section class="relative z-10 pb-20" data-projects-root>
|
||||
<Container>
|
||||
<div class="page-content-main">
|
||||
<div class="page-surface flex flex-wrap gap-2 mb-8 p-1 rounded-xl">
|
||||
{filterOptions.map((option, index) => (
|
||||
<button
|
||||
type="button"
|
||||
data-filter={option.key}
|
||||
aria-pressed={index === 0 ? "true" : "false"}
|
||||
class={`rounded-lg px-4 py-2 text-sm font-medium transition-colors ${
|
||||
index === 0
|
||||
? "bg-primary text-primary-foreground"
|
||||
: "text-muted-foreground hover:text-foreground hover:bg-muted"
|
||||
}`}
|
||||
>
|
||||
{option.label}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
<div class="space-y-6">
|
||||
{pageProjects.map((project) => (
|
||||
<article data-project-card data-type={project.type} class="page-surface p-6 md:p-8">
|
||||
<div class="grid gap-6 lg:grid-cols-[1.2fr_1fr]">
|
||||
<div>
|
||||
<p class="text-xs font-semibold uppercase tracking-wider text-primary">{project.systemType}</p>
|
||||
<h2 class="mt-2 text-2xl font-bold tracking-tight">{project.title}</h2>
|
||||
<p class="mt-3 text-muted-foreground">{project.context}</p>
|
||||
|
||||
<div class="grid gap-6 md:grid-cols-2 lg:grid-cols-3">
|
||||
{currentProjects.map((project) => (
|
||||
<ProjectCard project={project} lang={lang} showStatus={true} />
|
||||
))}
|
||||
</div>
|
||||
<div class="mt-5 flex flex-wrap gap-2">
|
||||
{project.tech.map((tech) => (
|
||||
<span class="rounded-md border border-border bg-muted/50 px-2.5 py-1 text-xs font-medium">{tech}</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="space-y-4">
|
||||
<div>
|
||||
<h3 class="text-sm font-bold uppercase tracking-wider text-foreground/80">{isZh ? '技术挑战' : 'Technical Challenges'}</h3>
|
||||
<ul class="mt-2 space-y-1.5 text-sm text-muted-foreground">
|
||||
{project.challenges?.map((item) => <li>• {item}</li>)}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3 class="text-sm font-bold uppercase tracking-wider text-foreground/80">{isZh ? '职责范围' : 'Responsibilities'}</h3>
|
||||
<ul class="mt-2 space-y-1.5 text-sm text-muted-foreground">
|
||||
{project.responsibilities?.map((item) => <li>• {item}</li>)}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3 class="text-sm font-bold uppercase tracking-wider text-foreground/80">{isZh ? '结果' : 'Outcomes'}</h3>
|
||||
<ul class="mt-2 space-y-1.5 text-sm text-foreground/90">
|
||||
{project.outcomes?.map((item) => <li>• {item}</li>)}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
))}
|
||||
</div>
|
||||
</Container>
|
||||
</section>
|
||||
</section>
|
||||
</Container>
|
||||
</main>
|
||||
|
||||
<Footer lang={lang} client:load />
|
||||
</Layout>
|
||||
|
||||
<script>
|
||||
const projectsRoot = document.querySelector("[data-projects-root]");
|
||||
if (projectsRoot) {
|
||||
const filterButtons = Array.from(projectsRoot.querySelectorAll("[data-filter]"));
|
||||
const projectCards = Array.from(projectsRoot.querySelectorAll("[data-project-card]"));
|
||||
const root = document.querySelector('[data-projects-root]');
|
||||
if (!root) {
|
||||
// no-op
|
||||
} else {
|
||||
const buttons = Array.from(root.querySelectorAll('[data-filter]'));
|
||||
const cards = Array.from(root.querySelectorAll('[data-project-card]'));
|
||||
|
||||
const applyFilter = (activeFilter) => {
|
||||
projectCards.forEach((card) => {
|
||||
const projectType = card.getAttribute("data-type");
|
||||
const shouldShow = activeFilter === "all" || projectType === activeFilter;
|
||||
card.classList.toggle("hidden", !shouldShow);
|
||||
const apply = (activeFilter) => {
|
||||
cards.forEach((card) => {
|
||||
const cardType = card.getAttribute('data-type');
|
||||
const visible = activeFilter === 'all' || activeFilter === cardType;
|
||||
card.classList.toggle('hidden', !visible);
|
||||
});
|
||||
|
||||
filterButtons.forEach((button) => {
|
||||
const isActive = button.getAttribute("data-filter") === activeFilter;
|
||||
button.setAttribute("aria-pressed", isActive ? "true" : "false");
|
||||
button.classList.toggle("bg-primary", isActive);
|
||||
button.classList.toggle("text-primary-foreground", isActive);
|
||||
button.classList.toggle("text-muted-foreground", !isActive);
|
||||
button.classList.toggle("hover:text-foreground", !isActive);
|
||||
button.classList.toggle("hover:bg-muted", !isActive);
|
||||
buttons.forEach((button) => {
|
||||
const isActive = button.getAttribute('data-filter') === activeFilter;
|
||||
button.setAttribute('aria-pressed', isActive ? 'true' : 'false');
|
||||
button.classList.toggle('bg-primary', isActive);
|
||||
button.classList.toggle('text-primary-foreground', isActive);
|
||||
button.classList.toggle('text-muted-foreground', !isActive);
|
||||
button.classList.toggle('hover:bg-muted', !isActive);
|
||||
button.classList.toggle('hover:text-foreground', !isActive);
|
||||
});
|
||||
};
|
||||
|
||||
filterButtons.forEach((button) => {
|
||||
button.addEventListener("click", () => {
|
||||
const selectedFilter = button.getAttribute("data-filter") ?? "all";
|
||||
applyFilter(selectedFilter);
|
||||
buttons.forEach((button) => {
|
||||
button.addEventListener('click', () => {
|
||||
apply(button.getAttribute('data-filter') ?? 'all');
|
||||
});
|
||||
});
|
||||
|
||||
applyFilter("all");
|
||||
apply('all');
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1,42 +1,14 @@
|
||||
---
|
||||
title: "我能做什么"
|
||||
description: "专注于构建 AI 产品和技术卓越的专业服务"
|
||||
title: "页面已迁移"
|
||||
description: "该页面已迁移到新的结构。"
|
||||
layout: "../../layouts/AboutLayout.astro"
|
||||
---
|
||||
|
||||
import HighlightBox from '../../components/markdown/HighlightBox.astro';
|
||||
# 页面已迁移
|
||||
|
||||
# 我能做什么 🚀
|
||||
原 **服务** 页面已拆分为更清晰的页面:
|
||||
|
||||
<HighlightBox type="tip">
|
||||
构建 AI 驱动产品,将雄心勃勃的想法变为现实。
|
||||
</HighlightBox>
|
||||
- [工具](/zh/uses)
|
||||
- [联系](/zh/contact)
|
||||
|
||||
## AI 产品开发 🤖
|
||||
|
||||
- 从概念到上线构建 AI 驱动产品
|
||||
- RAG 应用、AI Agent 和智能自动化
|
||||
- 使用现代技术栈的全栈开发(React、Next.js、Node.js)
|
||||
- 产品思维 - 专注于解决实际问题
|
||||
- 快速原型和迭代开发
|
||||
|
||||
## 技术咨询 📊
|
||||
|
||||
<HighlightBox type="info" title="我可以如何帮助">
|
||||
|
||||
- 技术架构和技术栈决策
|
||||
- 代码审查和最佳实践实施
|
||||
- 团队指导和知识转移
|
||||
- 性能优化和扩展策略
|
||||
- 投资技术尽职调查
|
||||
|
||||
</HighlightBox>
|
||||
|
||||
# 一起构建 💼
|
||||
|
||||
<HighlightBox type="success" title="联系我">
|
||||
我始终对讨论新项目和机会持开放态度。无论你是正在寻找技术合伙人、需要帮助构建 AI 产品,还是想要技术咨询 - 让我们聊聊。
|
||||
|
||||
- 邮箱:**zhaoguiyang18@gmail.com**
|
||||
- 查看我的[合作](/zh/hire)页面了解合作方式
|
||||
</HighlightBox>
|
||||
如需沟通远程岗位,请优先前往 [联系](/zh/contact)。
|
||||
|
||||
40
src/pages/zh/uses.astro
Normal file
40
src/pages/zh/uses.astro
Normal file
@@ -0,0 +1,40 @@
|
||||
---
|
||||
import Layout from '@/layouts/Layout.astro';
|
||||
import GlassHeader from '@/components/GlassHeader';
|
||||
import Footer from '@/components/Footer';
|
||||
import Container from '@/components/ui/Container.astro';
|
||||
import { uses } from '@/lib/data';
|
||||
import type { Lang } from '@/types/i18n';
|
||||
import { defaultLang } from '@/i18n/ui';
|
||||
|
||||
const lang = (Astro.currentLocale as Lang) || defaultLang;
|
||||
const isZh = lang === 'zh';
|
||||
---
|
||||
|
||||
<Layout title={isZh ? '工具' : 'Uses'}>
|
||||
<GlassHeader lang={lang} client:load transition:persist="header" />
|
||||
|
||||
<main class="min-h-screen pt-24 pb-20">
|
||||
<Container>
|
||||
<section class="page-content-main">
|
||||
<h1 class="text-4xl font-bold tracking-tight sm:text-5xl">{isZh ? '工具与工作流' : 'Uses'}</h1>
|
||||
<p class="mt-4 text-lg text-muted-foreground">
|
||||
{isZh ? '我在日常研发中稳定使用的工具与协作方式。' : 'Tools and workflows I use for daily engineering work.'}
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section class="page-content-main mt-10 grid gap-6 md:grid-cols-2">
|
||||
{uses.map((group) => (
|
||||
<article class="page-surface p-6">
|
||||
<h2 class="text-lg font-bold">{group.title[lang]}</h2>
|
||||
<ul class="mt-4 space-y-2 text-sm text-muted-foreground">
|
||||
{group.items.map((item) => <li>• {item}</li>)}
|
||||
</ul>
|
||||
</article>
|
||||
))}
|
||||
</section>
|
||||
</Container>
|
||||
</main>
|
||||
|
||||
<Footer lang={lang} client:load />
|
||||
</Layout>
|
||||
@@ -43,14 +43,28 @@ export interface ProjectImage {
|
||||
|
||||
export interface Project {
|
||||
id: string;
|
||||
tag: string;
|
||||
tag?: string;
|
||||
title: string;
|
||||
icon: string;
|
||||
type?: string;
|
||||
status?: string;
|
||||
role?: string;
|
||||
impact?: string;
|
||||
systemType?: string;
|
||||
context?: string;
|
||||
challenges?: string[];
|
||||
responsibilities?: string[];
|
||||
outcomes?: string[];
|
||||
color: string;
|
||||
image: ProjectImage;
|
||||
description: string[];
|
||||
tech: string[];
|
||||
link: string;
|
||||
featured?: boolean;
|
||||
links?: {
|
||||
github?: string;
|
||||
demo?: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface ServiceIcon {
|
||||
@@ -63,4 +77,21 @@ export interface Service {
|
||||
icon: ServiceIcon;
|
||||
items: string[];
|
||||
color: string;
|
||||
}
|
||||
}
|
||||
|
||||
export interface UsesGroup {
|
||||
title: LocalizedText;
|
||||
items: string[];
|
||||
}
|
||||
|
||||
export interface ContactIntent {
|
||||
id: string;
|
||||
title: LocalizedText;
|
||||
description: LocalizedText;
|
||||
}
|
||||
|
||||
export interface ContactMethod {
|
||||
label: LocalizedText;
|
||||
value: string;
|
||||
href?: string;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user