first commit
This commit is contained in:
commit
4d57422103
24
.gitignore
vendored
Normal file
24
.gitignore
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
.DS_Store
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
57
README.md
Normal file
57
README.md
Normal file
@ -0,0 +1,57 @@
|
||||
# React + TypeScript + Vite
|
||||
|
||||
This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
|
||||
|
||||
Currently, two official plugins are available:
|
||||
|
||||
- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Babel](https://babeljs.io/) for Fast Refresh
|
||||
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
|
||||
|
||||
## Expanding the ESLint configuration
|
||||
|
||||
If you are developing a production application, we recommend updating the configuration to enable type-aware lint rules:
|
||||
|
||||
```js
|
||||
export default tseslint.config({
|
||||
extends: [
|
||||
// Remove ...tseslint.configs.recommended and replace with this
|
||||
...tseslint.configs.recommendedTypeChecked,
|
||||
// Alternatively, use this for stricter rules
|
||||
...tseslint.configs.strictTypeChecked,
|
||||
// Optionally, add this for stylistic rules
|
||||
...tseslint.configs.stylisticTypeChecked,
|
||||
],
|
||||
languageOptions: {
|
||||
// other options...
|
||||
parserOptions: {
|
||||
project: ['./tsconfig.node.json', './tsconfig.app.json'],
|
||||
tsconfigRootDir: import.meta.dirname,
|
||||
},
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
You can also install [eslint-plugin-react-x](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x) and [eslint-plugin-react-dom](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-dom) for React-specific lint rules:
|
||||
|
||||
```js
|
||||
// eslint.config.js
|
||||
import reactX from 'eslint-plugin-react-x'
|
||||
import reactDom from 'eslint-plugin-react-dom'
|
||||
|
||||
export default tseslint.config({
|
||||
extends: [
|
||||
// other configs...
|
||||
// Enable lint rules for React
|
||||
reactX.configs['recommended-typescript'],
|
||||
// Enable lint rules for React DOM
|
||||
reactDom.configs.recommended,
|
||||
],
|
||||
languageOptions: {
|
||||
// other options...
|
||||
parserOptions: {
|
||||
project: ['./tsconfig.node.json', './tsconfig.app.json'],
|
||||
tsconfigRootDir: import.meta.dirname,
|
||||
},
|
||||
},
|
||||
})
|
||||
```
|
||||
28
eslint.config.js
Normal file
28
eslint.config.js
Normal file
@ -0,0 +1,28 @@
|
||||
import js from '@eslint/js'
|
||||
import globals from 'globals'
|
||||
import reactHooks from 'eslint-plugin-react-hooks'
|
||||
import reactRefresh from 'eslint-plugin-react-refresh'
|
||||
import tseslint from 'typescript-eslint'
|
||||
|
||||
export default tseslint.config(
|
||||
{ ignores: ['dist'] },
|
||||
{
|
||||
extends: [js.configs.recommended, ...tseslint.configs.recommended],
|
||||
files: ['**/*.{ts,tsx}'],
|
||||
languageOptions: {
|
||||
ecmaVersion: 2020,
|
||||
globals: globals.browser,
|
||||
},
|
||||
plugins: {
|
||||
'react-hooks': reactHooks,
|
||||
'react-refresh': reactRefresh,
|
||||
},
|
||||
rules: {
|
||||
...reactHooks.configs.recommended.rules,
|
||||
'react-refresh/only-export-components': [
|
||||
'warn',
|
||||
{ allowConstantExport: true },
|
||||
],
|
||||
},
|
||||
},
|
||||
)
|
||||
24
index.html
Normal file
24
index.html
Normal file
@ -0,0 +1,24 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>My Trae Project</title>
|
||||
<script type="module">
|
||||
if (import.meta.hot?.on) {
|
||||
import.meta.hot.on('vite:error', (error) => {
|
||||
if (error.err) {
|
||||
console.error(
|
||||
[error.err.message, error.err.frame].filter(Boolean).join('\n'),
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/main.tsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
4754
package-lock.json
generated
Normal file
4754
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
43
package.json
Normal file
43
package.json
Normal file
@ -0,0 +1,43 @@
|
||||
{
|
||||
"name": "hifastvpn",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "tsc -b && vite build",
|
||||
"lint": "eslint .",
|
||||
"preview": "vite preview",
|
||||
"check": "tsc -b --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"clsx": "^2.1.1",
|
||||
"framer-motion": "^12.23.24",
|
||||
"lucide-react": "^0.511.0",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
"react-router-dom": "^7.9.6",
|
||||
"tailwind-merge": "^3.0.2",
|
||||
"zustand": "^5.0.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.25.0",
|
||||
"@types/node": "^22.15.30",
|
||||
"@types/react": "^18.3.12",
|
||||
"@types/react-dom": "^18.3.1",
|
||||
"@vitejs/plugin-react": "^4.4.1",
|
||||
"autoprefixer": "^10.4.21",
|
||||
"babel-plugin-react-dev-locator": "^1.0.0",
|
||||
"eslint": "^9.25.0",
|
||||
"eslint-plugin-react-hooks": "^5.2.0",
|
||||
"eslint-plugin-react-refresh": "^0.4.19",
|
||||
"globals": "^16.0.0",
|
||||
"postcss": "^8.5.3",
|
||||
"tailwindcss": "^3.4.17",
|
||||
"typescript": "~5.8.3",
|
||||
"typescript-eslint": "^8.30.1",
|
||||
"vite": "^6.3.5",
|
||||
"vite-plugin-trae-solo-badge": "^1.0.0",
|
||||
"vite-tsconfig-paths": "^5.1.4"
|
||||
}
|
||||
}
|
||||
10
postcss.config.js
Normal file
10
postcss.config.js
Normal file
@ -0,0 +1,10 @@
|
||||
/** WARNING: DON'T EDIT THIS FILE */
|
||||
/** WARNING: DON'T EDIT THIS FILE */
|
||||
/** WARNING: DON'T EDIT THIS FILE */
|
||||
|
||||
export default {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
};
|
||||
4
public/favicon.svg
Normal file
4
public/favicon.svg
Normal file
@ -0,0 +1,4 @@
|
||||
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="32" height="32" fill="#0A0B0D"/>
|
||||
<path d="M26.6677 23.7149H8.38057V20.6496H5.33301V8.38159H26.6677V23.7149ZM8.38057 20.6496H23.6201V11.4482H8.38057V20.6496ZM16.0011 16.0021L13.8461 18.1705L11.6913 16.0021L13.8461 13.8337L16.0011 16.0021ZM22.0963 16.0008L19.9414 18.1691L17.7865 16.0008L19.9414 13.8324L22.0963 16.0008Z" fill="#32F08C"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 453 B |
26
src/App.tsx
Normal file
26
src/App.tsx
Normal file
@ -0,0 +1,26 @@
|
||||
import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
|
||||
import { LanguageProvider } from "@/contexts/LanguageContext";
|
||||
import Layout from "@/components/Layout";
|
||||
import Home from "@/pages/Home";
|
||||
import About from "@/pages/About";
|
||||
import Privacy from "@/pages/Privacy";
|
||||
import Terms from "@/pages/Terms";
|
||||
import Contact from "@/pages/Contact";
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
<LanguageProvider>
|
||||
<Router>
|
||||
<Layout>
|
||||
<Routes>
|
||||
<Route path="/" element={<Home />} />
|
||||
<Route path="/about" element={<About />} />
|
||||
<Route path="/privacy" element={<Privacy />} />
|
||||
<Route path="/terms" element={<Terms />} />
|
||||
<Route path="/contact" element={<Contact />} />
|
||||
</Routes>
|
||||
</Layout>
|
||||
</Router>
|
||||
</LanguageProvider>
|
||||
);
|
||||
}
|
||||
1
src/assets/react.svg
Normal file
1
src/assets/react.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="35.93" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 228"><path fill="#00D8FF" d="M210.483 73.824a171.49 171.49 0 0 0-8.24-2.597c.465-1.9.893-3.777 1.273-5.621c6.238-30.281 2.16-54.676-11.769-62.708c-13.355-7.7-35.196.329-57.254 19.526a171.23 171.23 0 0 0-6.375 5.848a155.866 155.866 0 0 0-4.241-3.917C100.759 3.829 77.587-4.822 63.673 3.233C50.33 10.957 46.379 33.89 51.995 62.588a170.974 170.974 0 0 0 1.892 8.48c-3.28.932-6.445 1.924-9.474 2.98C17.309 83.498 0 98.307 0 113.668c0 15.865 18.582 31.778 46.812 41.427a145.52 145.52 0 0 0 6.921 2.165a167.467 167.467 0 0 0-2.01 9.138c-5.354 28.2-1.173 50.591 12.134 58.266c13.744 7.926 36.812-.22 59.273-19.855a145.567 145.567 0 0 0 5.342-4.923a168.064 168.064 0 0 0 6.92 6.314c21.758 18.722 43.246 26.282 56.54 18.586c13.731-7.949 18.194-32.003 12.4-61.268a145.016 145.016 0 0 0-1.535-6.842c1.62-.48 3.21-.974 4.76-1.488c29.348-9.723 48.443-25.443 48.443-41.52c0-15.417-17.868-30.326-45.517-39.844Zm-6.365 70.984c-1.4.463-2.836.91-4.3 1.345c-3.24-10.257-7.612-21.163-12.963-32.432c5.106-11 9.31-21.767 12.459-31.957c2.619.758 5.16 1.557 7.61 2.4c23.69 8.156 38.14 20.213 38.14 29.504c0 9.896-15.606 22.743-40.946 31.14Zm-10.514 20.834c2.562 12.94 2.927 24.64 1.23 33.787c-1.524 8.219-4.59 13.698-8.382 15.893c-8.067 4.67-25.32-1.4-43.927-17.412a156.726 156.726 0 0 1-6.437-5.87c7.214-7.889 14.423-17.06 21.459-27.246c12.376-1.098 24.068-2.894 34.671-5.345a134.17 134.17 0 0 1 1.386 6.193ZM87.276 214.515c-7.882 2.783-14.16 2.863-17.955.675c-8.075-4.657-11.432-22.636-6.853-46.752a156.923 156.923 0 0 1 1.869-8.499c10.486 2.32 22.093 3.988 34.498 4.994c7.084 9.967 14.501 19.128 21.976 27.15a134.668 134.668 0 0 1-4.877 4.492c-9.933 8.682-19.886 14.842-28.658 17.94ZM50.35 144.747c-12.483-4.267-22.792-9.812-29.858-15.863c-6.35-5.437-9.555-10.836-9.555-15.216c0-9.322 13.897-21.212 37.076-29.293c2.813-.98 5.757-1.905 8.812-2.773c3.204 10.42 7.406 21.315 12.477 32.332c-5.137 11.18-9.399 22.249-12.634 32.792a134.718 134.718 0 0 1-6.318-1.979Zm12.378-84.26c-4.811-24.587-1.616-43.134 6.425-47.789c8.564-4.958 27.502 2.111 47.463 19.835a144.318 144.318 0 0 1 3.841 3.545c-7.438 7.987-14.787 17.08-21.808 26.988c-12.04 1.116-23.565 2.908-34.161 5.309a160.342 160.342 0 0 1-1.76-7.887Zm110.427 27.268a347.8 347.8 0 0 0-7.785-12.803c8.168 1.033 15.994 2.404 23.343 4.08c-2.206 7.072-4.956 14.465-8.193 22.045a381.151 381.151 0 0 0-7.365-13.322Zm-45.032-43.861c5.044 5.465 10.096 11.566 15.065 18.186a322.04 322.04 0 0 0-30.257-.006c4.974-6.559 10.069-12.652 15.192-18.18ZM82.802 87.83a323.167 323.167 0 0 0-7.227 13.238c-3.184-7.553-5.909-14.98-8.134-22.152c7.304-1.634 15.093-2.97 23.209-3.984a321.524 321.524 0 0 0-7.848 12.897Zm8.081 65.352c-8.385-.936-16.291-2.203-23.593-3.793c2.26-7.3 5.045-14.885 8.298-22.6a321.187 321.187 0 0 0 7.257 13.246c2.594 4.48 5.28 8.868 8.038 13.147Zm37.542 31.03c-5.184-5.592-10.354-11.779-15.403-18.433c4.902.192 9.899.29 14.978.29c5.218 0 10.376-.117 15.453-.343c-4.985 6.774-10.018 12.97-15.028 18.486Zm52.198-57.817c3.422 7.8 6.306 15.345 8.596 22.52c-7.422 1.694-15.436 3.058-23.88 4.071a382.417 382.417 0 0 0 7.859-13.026a347.403 347.403 0 0 0 7.425-13.565Zm-16.898 8.101a358.557 358.557 0 0 1-12.281 19.815a329.4 329.4 0 0 1-23.444.823c-7.967 0-15.716-.248-23.178-.732a310.202 310.202 0 0 1-12.513-19.846h.001a307.41 307.41 0 0 1-10.923-20.627a310.278 310.278 0 0 1 10.89-20.637l-.001.001a307.318 307.318 0 0 1 12.413-19.761c7.613-.576 15.42-.876 23.31-.876H128c7.926 0 15.743.303 23.354.883a329.357 329.357 0 0 1 12.335 19.695a358.489 358.489 0 0 1 11.036 20.54a329.472 329.472 0 0 1-11 20.722Zm22.56-122.124c8.572 4.944 11.906 24.881 6.52 51.026c-.344 1.668-.73 3.367-1.15 5.09c-10.622-2.452-22.155-4.275-34.23-5.408c-7.034-10.017-14.323-19.124-21.64-27.008a160.789 160.789 0 0 1 5.888-5.4c18.9-16.447 36.564-22.941 44.612-18.3ZM128 90.808c12.625 0 22.86 10.235 22.86 22.86s-10.235 22.86-22.86 22.86s-22.86-10.235-22.86-22.86s10.235-22.86 22.86-22.86Z"></path></svg>
|
||||
|
After Width: | Height: | Size: 4.0 KiB |
8
src/components/Empty.tsx
Normal file
8
src/components/Empty.tsx
Normal file
@ -0,0 +1,8 @@
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
// Empty component
|
||||
export default function Empty() {
|
||||
return (
|
||||
<div className={cn('flex h-full items-center justify-center')}>Empty</div>
|
||||
)
|
||||
}
|
||||
153
src/components/Layout.tsx
Normal file
153
src/components/Layout.tsx
Normal file
@ -0,0 +1,153 @@
|
||||
import { ReactNode } from 'react';
|
||||
import { Link, useLocation } from 'react-router-dom';
|
||||
import { Shield, Globe, Zap, Lock, Smartphone, Laptop, Download, Mail, MapPin, Phone, Languages } from 'lucide-react';
|
||||
import { useLanguage } from '@/contexts/LanguageContext';
|
||||
|
||||
interface LayoutProps {
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
export default function Layout({ children }: LayoutProps) {
|
||||
const location = useLocation();
|
||||
const { language, setLanguage, t } = useLanguage();
|
||||
|
||||
const isActive = (path: string) => location.pathname === path;
|
||||
|
||||
const toggleLanguage = () => {
|
||||
setLanguage(language === 'en' ? 'zh' : 'en');
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gradient-to-br from-slate-900 via-blue-900 to-indigo-900">
|
||||
{/* Navigation */}
|
||||
<nav className="bg-white/10 backdrop-blur-md border-b border-white/20 sticky top-0 z-50">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div className="flex justify-between items-center h-16">
|
||||
<div className="flex items-center">
|
||||
<Link to="/" className="flex items-center space-x-2">
|
||||
<Shield className="h-8 w-8 text-blue-400" />
|
||||
<span className="text-2xl font-bold text-white">HiFast VPN</span>
|
||||
</Link>
|
||||
</div>
|
||||
<div className="hidden md:flex items-center space-x-8">
|
||||
<Link
|
||||
to="/"
|
||||
className={`px-3 py-2 rounded-md text-sm font-medium transition-colors ${
|
||||
isActive('/') ? 'text-blue-400 bg-white/10' : 'text-white hover:text-blue-400'
|
||||
}`}
|
||||
>
|
||||
{t('nav.home')}
|
||||
</Link>
|
||||
<Link
|
||||
to="/about"
|
||||
className={`px-3 py-2 rounded-md text-sm font-medium transition-colors ${
|
||||
isActive('/about') ? 'text-blue-400 bg-white/10' : 'text-white hover:text-blue-400'
|
||||
}`}
|
||||
>
|
||||
{t('nav.about')}
|
||||
</Link>
|
||||
<Link
|
||||
to="/privacy"
|
||||
className={`px-3 py-2 rounded-md text-sm font-medium transition-colors ${
|
||||
isActive('/privacy') ? 'text-blue-400 bg-white/10' : 'text-white hover:text-blue-400'
|
||||
}`}
|
||||
>
|
||||
{t('nav.privacy')}
|
||||
</Link>
|
||||
<Link
|
||||
to="/terms"
|
||||
className={`px-3 py-2 rounded-md text-sm font-medium transition-colors ${
|
||||
isActive('/terms') ? 'text-blue-400 bg-white/10' : 'text-white hover:text-blue-400'
|
||||
}`}
|
||||
>
|
||||
{t('nav.terms')}
|
||||
</Link>
|
||||
<Link
|
||||
to="/contact"
|
||||
className={`px-3 py-2 rounded-md text-sm font-medium transition-colors ${
|
||||
isActive('/contact') ? 'text-blue-400 bg-white/10' : 'text-white hover:text-blue-400'
|
||||
}`}
|
||||
>
|
||||
{t('nav.contact')}
|
||||
</Link>
|
||||
|
||||
{/* Language Switcher */}
|
||||
<button
|
||||
onClick={toggleLanguage}
|
||||
className="flex items-center space-x-2 px-3 py-2 rounded-md text-sm font-medium text-white hover:text-blue-400 hover:bg-white/10 transition-colors border border-white/20 hover:border-blue-400/50"
|
||||
title={t('common.switchLanguage')}
|
||||
>
|
||||
<Languages className="h-4 w-4" />
|
||||
<span className="text-xs font-bold">
|
||||
{language === 'en' ? 'EN' : '中文'}
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
{/* Main Content */}
|
||||
<main>{children}</main>
|
||||
|
||||
{/* Footer */}
|
||||
<footer className="bg-black/20 backdrop-blur-md border-t border-white/20 mt-20">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-12">
|
||||
<div className="grid grid-cols-1 md:grid-cols-4 gap-8">
|
||||
<div className="col-span-1 md:col-span-2">
|
||||
<div className="flex items-center space-x-2 mb-4">
|
||||
<Shield className="h-8 w-8 text-blue-400" />
|
||||
<span className="text-2xl font-bold text-white">HiFast VPN</span>
|
||||
</div>
|
||||
<p className="text-gray-300 mb-4">
|
||||
Premium VPN service providing secure, fast, and reliable internet access worldwide.
|
||||
Operated by TAW TRADERS SDN. BHD.
|
||||
</p>
|
||||
<div className="flex space-x-4">
|
||||
<div className="flex items-center space-x-2 text-gray-300">
|
||||
<Globe className="h-5 w-5" />
|
||||
<span>{t('footer.countries')}</span>
|
||||
</div>
|
||||
<div className="flex items-center space-x-2 text-gray-300">
|
||||
<Zap className="h-5 w-5" />
|
||||
<span>{t('footer.speed')}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3 className="text-white font-semibold mb-4">{t('footer.products')}</h3>
|
||||
<ul className="space-y-2 text-gray-300">
|
||||
<li><a href="#" className="hover:text-blue-400 transition-colors">{t('common.ios')} {t('common.app')}</a></li>
|
||||
<li><a href="#" className="hover:text-blue-400 transition-colors">{t('common.android')} {t('common.app')}</a></li>
|
||||
<li><a href="#" className="hover:text-blue-400 transition-colors">{t('common.windows')}</a></li>
|
||||
<li><a href="#" className="hover:text-blue-400 transition-colors">{t('common.macos')}</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3 className="text-white font-semibold mb-4">{t('footer.support')}</h3>
|
||||
<ul className="space-y-2 text-gray-300">
|
||||
<li><Link to="/contact" className="hover:text-blue-400 transition-colors">{t('nav.contact')}</Link></li>
|
||||
<li><a href="#" className="hover:text-blue-400 transition-colors">{t('footer.help')}</a></li>
|
||||
<li><Link to="/privacy" className="hover:text-blue-400 transition-colors">{t('nav.privacy')}</Link></li>
|
||||
<li><Link to="/terms" className="hover:text-blue-400 transition-colors">{t('nav.terms')}</Link></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="border-t border-white/20 mt-8 pt-8">
|
||||
<div className="flex flex-col md:flex-row justify-between items-center">
|
||||
<p className="text-gray-400 text-sm">
|
||||
{t('footer.rights')}
|
||||
</p>
|
||||
<p className="text-gray-400 text-sm mt-2 md:mt-0">
|
||||
{t('footer.operated')}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
496
src/contexts/LanguageContext.tsx
Normal file
496
src/contexts/LanguageContext.tsx
Normal file
@ -0,0 +1,496 @@
|
||||
import React, { createContext, useContext, useState, ReactNode } from 'react';
|
||||
|
||||
export type Language = 'en' | 'zh';
|
||||
|
||||
interface LanguageContextType {
|
||||
language: Language;
|
||||
setLanguage: (lang: Language) => void;
|
||||
t: (key: string) => string;
|
||||
}
|
||||
|
||||
const LanguageContext = createContext<LanguageContextType | undefined>(undefined);
|
||||
|
||||
export const useLanguage = () => {
|
||||
const context = useContext(LanguageContext);
|
||||
if (context === undefined) {
|
||||
throw new Error('useLanguage must be used within a LanguageProvider');
|
||||
}
|
||||
return context;
|
||||
};
|
||||
|
||||
interface LanguageProviderProps {
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
export const LanguageProvider: React.FC<LanguageProviderProps> = ({ children }) => {
|
||||
const [language, setLanguage] = useState<Language>('en');
|
||||
|
||||
const translations = {
|
||||
en: {
|
||||
// Navigation
|
||||
'nav.home': 'Home',
|
||||
'nav.about': 'About Us',
|
||||
'nav.privacy': 'Privacy Policy',
|
||||
'nav.terms': 'Terms',
|
||||
'nav.contact': 'Contact',
|
||||
|
||||
// Footer
|
||||
'footer.products': 'Products',
|
||||
'footer.support': 'Support',
|
||||
'footer.help': 'Help Center',
|
||||
'footer.rights': '© 2025 TAW TRADERS SDN. BHD. All rights reserved.',
|
||||
'footer.operated': 'Operated by TAW TRADERS SDN. BHD. (Malaysia)',
|
||||
'footer.countries': '105+ Countries',
|
||||
'footer.speed': 'Lightning Fast',
|
||||
|
||||
// Home Page
|
||||
'home.hero.title': 'Secure Your Digital Life with',
|
||||
'home.hero.subtitle': 'HiFast VPN',
|
||||
'home.hero.description': 'Experience the internet without boundaries. Fast, secure, and private access to the world\'s content with military-grade encryption and zero-log policy.',
|
||||
'home.hero.cta': 'Get Started Now',
|
||||
'home.hero.learnMore': 'Learn More',
|
||||
'home.features.title': 'Why Choose HiFast VPN?',
|
||||
'home.features.subtitle': 'We provide enterprise-grade VPN services with unmatched speed, security, and reliability.',
|
||||
'home.features.global': 'Global Server Network',
|
||||
'home.features.global.desc': 'Access 105+ countries with our worldwide server network for unrestricted internet access.',
|
||||
'home.features.speed': 'Lightning Fast Speeds',
|
||||
'home.features.speed.desc': 'Optimized servers providing blazing fast connection speeds for streaming and browsing.',
|
||||
'home.features.encryption': 'Military-Grade Encryption',
|
||||
'home.features.encryption.desc': 'AES-256 encryption keeps your data secure and private from prying eyes.',
|
||||
'home.features.logs': 'No Activity Logs',
|
||||
'home.features.logs.desc': 'Zero-log policy ensures your online activities remain completely private.',
|
||||
'home.platforms.title': 'Available on All Your Devices',
|
||||
'home.platforms.subtitle': 'Protect your privacy across all platforms with our native applications.',
|
||||
'home.cta.title': 'Start Your Secure Journey Today',
|
||||
'home.cta.subtitle': 'Join millions of users who trust HiFast VPN for their online privacy and security.',
|
||||
'home.cta.button': 'Get Protected Now',
|
||||
'home.guarantee.moneyback': '30-day money-back guarantee',
|
||||
'home.guarantee.free': 'No credit card required',
|
||||
'home.stats.users': 'Happy Users',
|
||||
'home.stats.countries': 'Countries',
|
||||
'home.stats.uptime': 'Uptime',
|
||||
'home.stats.support': 'Support',
|
||||
'home.status.available': 'Available',
|
||||
'home.status.comingSoon': 'Coming Soon',
|
||||
|
||||
// About Page
|
||||
'about.title': 'About',
|
||||
'about.subtitle': 'HiFast VPN',
|
||||
'about.description': 'Your trusted partner in digital privacy and security. We\'re dedicated to providing fast, secure, and reliable VPN services to users worldwide.',
|
||||
'about.company.title': 'Company Overview',
|
||||
'about.company.name': 'Company Name',
|
||||
'about.company.location': 'Registered Location',
|
||||
'about.company.focus': 'Business Focus',
|
||||
'about.mission.title': 'Our Mission',
|
||||
'about.mission.text': 'To provide secure, fast, and reliable internet access to users worldwide while maintaining the highest standards of privacy protection and customer service.',
|
||||
'about.stats.experience': 'Years of Experience',
|
||||
'about.stats.users': 'Global Users',
|
||||
'about.stats.servers': 'Server Locations',
|
||||
'about.stats.countries': 'Countries Served',
|
||||
'about.values.title': 'Our Core Values',
|
||||
'about.values.security': 'Security First',
|
||||
'about.values.security.desc': 'We prioritize user privacy and data protection with military-grade encryption and zero-log policies.',
|
||||
'about.values.global': 'Global Access',
|
||||
'about.values.global.desc': 'Providing unrestricted internet access worldwide while maintaining the highest security standards.',
|
||||
'about.values.customer': 'Customer Focus',
|
||||
'about.values.customer.desc': 'Dedicated to providing exceptional service and support to our users around the clock.',
|
||||
'about.values.excellence': 'Excellence',
|
||||
'about.values.excellence.desc': 'Committed to delivering premium VPN services with unmatched speed and reliability.',
|
||||
'about.legal.title': 'Legal Information',
|
||||
'about.legal.business': 'Business Registration',
|
||||
'about.legal.compliance': 'Compliance',
|
||||
'about.legal.business.desc': 'TAW TRADERS SDN. BHD. is a legally registered company in Malaysia, operating in the network communications and VPN services sector.',
|
||||
'about.legal.compliance.desc': 'We adhere to international privacy standards and regulations, ensuring our users\' data protection and privacy rights.',
|
||||
|
||||
// Privacy Page
|
||||
'privacy.title': 'Privacy Policy',
|
||||
'privacy.updated': 'Last updated: January 1, 2025',
|
||||
'privacy.description': 'At HiFast VPN, operated by TAW TRADERS SDN. BHD., we are committed to protecting your privacy and ensuring the security of your personal information. This Privacy Policy explains how we collect, use, and safeguard your data.',
|
||||
'privacy.commitment.title': 'Our Commitment to Privacy',
|
||||
'privacy.commitment.text': 'TAW TRADERS SDN. BHD. is committed to maintaining the highest standards of privacy protection. We believe that privacy is a fundamental right, and our VPN service is designed with this principle at its core.',
|
||||
'privacy.nolog': 'Zero-log policy for browsing activities',
|
||||
'privacy.laws': 'Malaysia-based company with strong privacy laws',
|
||||
'privacy.contact.title': 'Contact Us',
|
||||
'privacy.contact.text': 'If you have any questions about this Privacy Policy or our privacy practices, please contact us:',
|
||||
'privacy.company': 'Company',
|
||||
'privacy.email': 'Email',
|
||||
'privacy.policy': 'Policy Updates',
|
||||
'privacy.policy.text': 'We will notify users of any material changes to this policy',
|
||||
'privacy.acknowledgement': 'By using HiFast VPN services, you acknowledge that you have read and understood this Privacy Policy. Your privacy is our priority, and we are committed to protecting your digital rights.',
|
||||
|
||||
// Terms Page
|
||||
'terms.title': 'Terms of Service',
|
||||
'terms.updated': 'Last updated: January 1, 2025',
|
||||
'terms.description': 'Please read these Terms of Service carefully before using HiFast VPN services provided by TAW TRADERS SDN. BHD.',
|
||||
'terms.notice.title': 'Important Notice',
|
||||
'terms.notice.text': 'By accessing or using HiFast VPN services, you acknowledge that you have read, understood, and agree to these Terms of Service. If you do not agree with any part of these terms, you must not use our services.',
|
||||
'terms.acceptance.title': 'Acceptance of Terms',
|
||||
'terms.service.title': 'Service Description',
|
||||
'terms.responsibilities.title': 'User Responsibilities',
|
||||
'terms.prohibited.title': 'Prohibited Activities',
|
||||
'terms.limitations.title': 'Service Limitations',
|
||||
'terms.payment.title': 'Payment and Refunds',
|
||||
'terms.privacy.title': 'Privacy and Data Protection',
|
||||
'terms.termination.title': 'Termination',
|
||||
'terms.final.title': 'Final Provisions',
|
||||
'terms.governing': 'Governing Law:',
|
||||
'terms.governing.text': 'These Terms of Service shall be governed by and construed in accordance with the laws of Malaysia.',
|
||||
'terms.changes': 'Changes to Terms:',
|
||||
'terms.changes.text': 'We reserve the right to modify these terms at any time. Changes will be posted on this page with updated effective dates.',
|
||||
'terms.contact': 'Contact:',
|
||||
'terms.contact.text': 'For questions about these terms, please contact us at legal@hifastvpn.com',
|
||||
'terms.acknowledgement': 'By using HiFast VPN services, you acknowledge that you have read, understood, and agree to these Terms of Service.',
|
||||
|
||||
// Contact Page
|
||||
'contact.title': 'Contact',
|
||||
'contact.subtitle': 'Us',
|
||||
'contact.description': 'We\'re here to help! Get in touch with our support team for any questions, technical assistance, or business inquiries.',
|
||||
'contact.email.title': 'Email Support',
|
||||
'contact.email.value': 'support@hifastvpn.com',
|
||||
'contact.email.desc': 'Get help with any questions or issues',
|
||||
'contact.hours.title': 'Support Hours',
|
||||
'contact.hours.value': '24/7 Support',
|
||||
'contact.hours.desc': 'Round-the-clock customer assistance',
|
||||
'contact.address.title': 'Company Address',
|
||||
'contact.address.value': 'TAW TRADERS SDN. BHD.',
|
||||
'contact.address.desc': 'Malaysia (Registered Company)',
|
||||
'contact.form.title': 'Send Us a Message',
|
||||
'contact.form.name': 'Your Name',
|
||||
'contact.form.name.placeholder': 'Enter your name',
|
||||
'contact.form.email': 'Email Address',
|
||||
'contact.form.email.placeholder': 'your.email@example.com',
|
||||
'contact.form.subject': 'Subject',
|
||||
'contact.form.subject.placeholder': 'How can we help you?',
|
||||
'contact.form.message': 'Message',
|
||||
'contact.form.message.placeholder': 'Tell us more about your inquiry...',
|
||||
'contact.form.submit': 'Send Message',
|
||||
'contact.company.title': 'Company Information',
|
||||
'contact.company.registered': 'Registered Company',
|
||||
'contact.company.name': 'TAW TRADERS SDN. BHD.',
|
||||
'contact.company.location': 'Malaysia',
|
||||
'contact.quick.title': 'Quick Support',
|
||||
'contact.quick.text': 'For immediate assistance, check our common support topics:',
|
||||
'contact.quick.connection': 'Connection troubleshooting',
|
||||
'contact.quick.billing': 'Account and billing questions',
|
||||
'contact.quick.installation': 'App installation guides',
|
||||
'contact.quick.servers': 'Server location information',
|
||||
'contact.response.title': 'Response Time',
|
||||
'contact.response.text': 'We strive to respond to all inquiries as quickly as possible:',
|
||||
'contact.response.general': 'General inquiries:',
|
||||
'contact.response.technical': 'Technical support:',
|
||||
'contact.response.billing': 'Billing issues:',
|
||||
'contact.response.general.time': 'Within 24 hours',
|
||||
'contact.response.technical.time': 'Within 12 hours',
|
||||
'contact.response.billing.time': 'Within 6 hours',
|
||||
'contact.success': 'Thank you for your message! We will get back to you soon.',
|
||||
|
||||
// Common
|
||||
'common.app': 'App',
|
||||
'common.windows': 'Windows',
|
||||
'common.macos': 'macOS',
|
||||
'common.ios': 'iOS',
|
||||
'common.android': 'Android',
|
||||
'common.download': 'Download',
|
||||
'common.getStarted': 'Get Started',
|
||||
'common.learnMore': 'Learn More',
|
||||
'common.submit': 'Submit',
|
||||
'common.send': 'Send',
|
||||
'common.cancel': 'Cancel',
|
||||
'common.close': 'Close',
|
||||
'common.yes': 'Yes',
|
||||
'common.no': 'No',
|
||||
'common.ok': 'OK',
|
||||
'common.save': 'Save',
|
||||
'common.edit': 'Edit',
|
||||
'common.delete': 'Delete',
|
||||
'common.add': 'Add',
|
||||
'common.remove': 'Remove',
|
||||
'common.back': 'Back',
|
||||
'common.next': 'Next',
|
||||
'common.previous': 'Previous',
|
||||
'common.finish': 'Finish',
|
||||
'common.confirm': 'Confirm',
|
||||
'common.error': 'Error',
|
||||
'common.success': 'Success',
|
||||
'common.warning': 'Warning',
|
||||
'common.info': 'Info',
|
||||
'common.required': 'Required',
|
||||
'common.optional': 'Optional',
|
||||
'common.loading': 'Loading...',
|
||||
'common.search': 'Search',
|
||||
'common.filter': 'Filter',
|
||||
'common.sort': 'Sort',
|
||||
'common.view': 'View',
|
||||
'common.edit': 'Edit',
|
||||
'common.update': 'Update',
|
||||
'common.create': 'Create',
|
||||
'common.remove': 'Remove',
|
||||
'common.refresh': 'Refresh',
|
||||
'common.retry': 'Retry',
|
||||
'common.continue': 'Continue',
|
||||
'common.skip': 'Skip',
|
||||
'common.start': 'Start',
|
||||
'common.stop': 'Stop',
|
||||
'common.pause': 'Pause',
|
||||
'common.resume': 'Resume',
|
||||
'common.reset': 'Reset',
|
||||
'common.clear': 'Clear',
|
||||
'common.select': 'Select',
|
||||
'common.deselect': 'Deselect',
|
||||
'common.all': 'All',
|
||||
'common.none': 'None',
|
||||
'common.show': 'Show',
|
||||
'common.hide': 'Hide',
|
||||
'common.open': 'Open',
|
||||
'common.close': 'Close',
|
||||
'common.expand': 'Expand',
|
||||
'common.collapse': 'Collapse',
|
||||
'common.enable': 'Enable',
|
||||
'common.disable': 'Disable',
|
||||
'common.on': 'On',
|
||||
'common.off': 'Off',
|
||||
'common.language': 'Language',
|
||||
'common.english': 'English',
|
||||
'common.chinese': '中文',
|
||||
'common.switchLanguage': 'Switch Language'
|
||||
},
|
||||
zh: {
|
||||
// Navigation
|
||||
'nav.home': '首页',
|
||||
'nav.about': '关于我们',
|
||||
'nav.privacy': '隐私政策',
|
||||
'nav.terms': '服务条款',
|
||||
'nav.contact': '联系我们',
|
||||
|
||||
// Footer
|
||||
'footer.products': '产品',
|
||||
'footer.support': '支持',
|
||||
'footer.help': '帮助中心',
|
||||
'footer.rights': '© 2025 TAW TRADERS SDN. BHD. 版权所有',
|
||||
'footer.operated': '由 TAW TRADERS SDN. BHD.(马来西亚)运营',
|
||||
'footer.countries': '105+ 国家',
|
||||
'footer.speed': '闪电般快速',
|
||||
|
||||
// Home Page
|
||||
'home.hero.title': '保护您的数字生活',
|
||||
'home.hero.subtitle': 'HiFast VPN',
|
||||
'home.hero.description': '体验无界限的互联网。快速、安全、私密地访问全球内容,采用军用级加密和零日志政策。',
|
||||
'home.hero.cta': '立即开始',
|
||||
'home.hero.learnMore': '了解更多',
|
||||
'home.features.title': '为什么选择 HiFast VPN?',
|
||||
'home.features.subtitle': '我们提供无与伦比的速度、安全性和可靠性的企业级 VPN 服务。',
|
||||
'home.features.global': '全球服务器网络',
|
||||
'home.features.global.desc': '通过我们的全球服务器网络访问 105+ 个国家,享受无限制的互联网访问。',
|
||||
'home.features.speed': '闪电般快速',
|
||||
'home.features.speed.desc': '优化的服务器为流媒体和浏览提供极快的连接速度。',
|
||||
'home.features.encryption': '军用级加密',
|
||||
'home.features.encryption.desc': 'AES-256 加密保护您的数据安全,防止他人窥探。',
|
||||
'home.features.logs': '无活动日志',
|
||||
'home.features.logs.desc': '零日志政策确保您的在线活动保持完全私密。',
|
||||
'home.platforms.title': '适用于您的所有设备',
|
||||
'home.platforms.subtitle': '使用我们的原生应用程序在所有平台上保护您的隐私。',
|
||||
'home.cta.title': '今天开始您的安全之旅',
|
||||
'home.cta.subtitle': '加入数百万信任 HiFast VPN 保护在线隐私和安全的用户。',
|
||||
'home.cta.button': '立即获得保护',
|
||||
'home.guarantee.moneyback': '30 天退款保证',
|
||||
'home.guarantee.free': '无需信用卡',
|
||||
'home.stats.users': '满意用户',
|
||||
'home.stats.countries': '国家',
|
||||
'home.stats.uptime': '正常运行时间',
|
||||
'home.stats.support': '支持',
|
||||
'home.status.available': '可用',
|
||||
'home.status.comingSoon': '即将推出',
|
||||
|
||||
// About Page
|
||||
'about.title': '关于',
|
||||
'about.subtitle': 'HiFast VPN',
|
||||
'about.description': '您在数字隐私和安全方面值得信赖的合作伙伴。我们致力于为世界各地的用户提供快速、安全、可靠的 VPN 服务。',
|
||||
'about.company.title': '公司概况',
|
||||
'about.company.name': '公司名称',
|
||||
'about.company.location': '注册地点',
|
||||
'about.company.focus': '业务重点',
|
||||
'about.mission.title': '我们的使命',
|
||||
'about.mission.text': '为全球用户提供安全、快速、可靠的互联网访问,同时保持最高标准的隐私保护和客户服务。',
|
||||
'about.stats.experience': '多年经验',
|
||||
'about.stats.users': '全球用户',
|
||||
'about.stats.servers': '服务器位置',
|
||||
'about.stats.countries': '服务国家',
|
||||
'about.values.title': '我们的核心价值观',
|
||||
'about.values.security': '安全第一',
|
||||
'about.values.security.desc': '我们优先考虑用户隐私和数据保护,采用军用级加密和零日志政策。',
|
||||
'about.values.global': '全球访问',
|
||||
'about.values.global.desc': '在保持最高安全标准的同时,提供全球无限制的互联网访问。',
|
||||
'about.values.customer': '客户至上',
|
||||
'about.values.customer.desc': '致力于全天候为用户提供卓越的服务和支持。',
|
||||
'about.values.excellence': '追求卓越',
|
||||
'about.values.excellence.desc': '致力于提供具有无与伦比速度和可靠性的优质 VPN 服务。',
|
||||
'about.legal.title': '法律信息',
|
||||
'about.legal.business': '商业注册',
|
||||
'about.legal.compliance': '合规性',
|
||||
'about.legal.business.desc': 'TAW TRADERS SDN. BHD. 是在马来西亚合法注册的公司,在网络通信和 VPN 服务领域运营。',
|
||||
'about.legal.compliance.desc': '我们遵守国际隐私标准和法规,确保用户的数据保护和隐私权。',
|
||||
|
||||
// Privacy Page
|
||||
'privacy.title': '隐私政策',
|
||||
'privacy.updated': '最后更新:2025年1月1日',
|
||||
'privacy.description': '在由 TAW TRADERS SDN. BHD. 运营的 HiFast VPN,我们致力于保护您的隐私并确保您的个人信息安全。本隐私政策解释了我们如何收集、使用和保护您的数据。',
|
||||
'privacy.commitment.title': '我们对隐私的承诺',
|
||||
'privacy.commitment.text': 'TAW TRADERS SDN. BHD. 致力于维护最高标准的隐私保护。我们相信隐私是一项基本权利,我们的 VPN 服务以这一原则为核心设计。',
|
||||
'privacy.nolog': '浏览活动的零日志政策',
|
||||
'privacy.laws': '基于马来西亚的强隐私法律公司',
|
||||
'privacy.contact.title': '联系我们',
|
||||
'privacy.contact.text': '如果您对本隐私政策或我们的隐私实践有任何疑问,请联系我们:',
|
||||
'privacy.company': '公司',
|
||||
'privacy.email': '电子邮件',
|
||||
'privacy.policy': '政策更新',
|
||||
'privacy.policy.text': '我们将通知用户本政策的任何重大变更',
|
||||
'privacy.acknowledgement': '使用 HiFast VPN 服务,即表示您承认您已阅读并理解本隐私政策。您的隐私是我们的首要任务,我们致力于保护您的数字权利。',
|
||||
|
||||
// Terms Page
|
||||
'terms.title': '服务条款',
|
||||
'terms.updated': '最后更新:2025年1月1日',
|
||||
'terms.description': '请在使用由 TAW TRADERS SDN. BHD. 提供的 HiFast VPN 服务之前仔细阅读这些服务条款。',
|
||||
'terms.notice.title': '重要通知',
|
||||
'terms.notice.text': '访问或使用 HiFast VPN 服务,即表示您承认您已阅读、理解并同意这些服务条款。如果您不同意这些条款的任何部分,您不得使用我们的服务。',
|
||||
'terms.acceptance.title': '接受条款',
|
||||
'terms.service.title': '服务描述',
|
||||
'terms.responsibilities.title': '用户责任',
|
||||
'terms.prohibited.title': '禁止活动',
|
||||
'terms.limitations.title': '服务限制',
|
||||
'terms.payment.title': '付款和退款',
|
||||
'terms.privacy.title': '隐私和数据保护',
|
||||
'terms.termination.title': '终止',
|
||||
'terms.final.title': '最终条款',
|
||||
'terms.governing': '适用法律:',
|
||||
'terms.governing.text': '这些服务条款应受马来西亚法律的管辖和解释。',
|
||||
'terms.changes': '条款变更:',
|
||||
'terms.changes.text': '我们保留随时修改这些条款的权利。变更将发布在此页面上,并附有更新的生效日期。',
|
||||
'terms.contact': '联系:',
|
||||
'terms.contact.text': '有关这些条款的问题,请通过 legal@hifastvpn.com 联系我们',
|
||||
'terms.acknowledgement': '使用 HiFast VPN 服务,即表示您承认您已阅读、理解并同意这些服务条款。',
|
||||
|
||||
// Contact Page
|
||||
'contact.title': '联系',
|
||||
'contact.subtitle': '我们',
|
||||
'contact.description': '我们随时为您提供帮助!与我们的支持团队联系,获取任何问题、技术帮助或业务咨询。',
|
||||
'contact.email.title': '电子邮件支持',
|
||||
'contact.email.value': 'support@hifastvpn.com',
|
||||
'contact.email.desc': '获得任何问题的帮助',
|
||||
'contact.hours.title': '支持时间',
|
||||
'contact.hours.value': '24/7 支持',
|
||||
'contact.hours.desc': '全天候客户帮助',
|
||||
'contact.address.title': '公司地址',
|
||||
'contact.address.value': 'TAW TRADERS SDN. BHD.',
|
||||
'contact.address.desc': '马来西亚(注册公司)',
|
||||
'contact.form.title': '给我们发送消息',
|
||||
'contact.form.name': '您的姓名',
|
||||
'contact.form.name.placeholder': '输入您的姓名',
|
||||
'contact.form.email': '电子邮件地址',
|
||||
'contact.form.email.placeholder': 'your.email@example.com',
|
||||
'contact.form.subject': '主题',
|
||||
'contact.form.subject.placeholder': '我们如何能帮到您?',
|
||||
'contact.form.message': '消息',
|
||||
'contact.form.message.placeholder': '告诉我们更多关于您的询问...',
|
||||
'contact.form.submit': '发送消息',
|
||||
'contact.company.title': '公司信息',
|
||||
'contact.company.registered': '注册公司',
|
||||
'contact.company.name': 'TAW TRADERS SDN. BHD.',
|
||||
'contact.company.location': '马来西亚',
|
||||
'contact.quick.title': '快速支持',
|
||||
'contact.quick.text': '如需即时帮助,请查看我们的常见支持主题:',
|
||||
'contact.quick.connection': '连接故障排除',
|
||||
'contact.quick.billing': '账户和账单问题',
|
||||
'contact.quick.installation': '应用程序安装指南',
|
||||
'contact.quick.servers': '服务器位置信息',
|
||||
'contact.response.title': '响应时间',
|
||||
'contact.response.text': '我们努力尽快回复所有询问:',
|
||||
'contact.response.general': '一般询问:',
|
||||
'contact.response.technical': '技术支持:',
|
||||
'contact.response.billing': '账单问题:',
|
||||
'contact.response.general.time': '24小时内',
|
||||
'contact.response.technical.time': '12小时内',
|
||||
'contact.response.billing.time': '6小时内',
|
||||
'contact.success': '感谢您的消息!我们将尽快回复您。',
|
||||
|
||||
// Common
|
||||
'common.app': '应用',
|
||||
'common.windows': 'Windows',
|
||||
'common.macos': 'macOS',
|
||||
'common.ios': 'iOS',
|
||||
'common.android': 'Android',
|
||||
'common.download': '下载',
|
||||
'common.getStarted': '开始使用',
|
||||
'common.learnMore': '了解更多',
|
||||
'common.submit': '提交',
|
||||
'common.send': '发送',
|
||||
'common.cancel': '取消',
|
||||
'common.close': '关闭',
|
||||
'common.yes': '是',
|
||||
'common.no': '否',
|
||||
'common.ok': '确定',
|
||||
'common.save': '保存',
|
||||
'common.edit': '编辑',
|
||||
'common.delete': '删除',
|
||||
'common.add': '添加',
|
||||
'common.remove': '移除',
|
||||
'common.back': '返回',
|
||||
'common.next': '下一步',
|
||||
'common.previous': '上一步',
|
||||
'common.finish': '完成',
|
||||
'common.confirm': '确认',
|
||||
'common.error': '错误',
|
||||
'common.success': '成功',
|
||||
'common.warning': '警告',
|
||||
'common.info': '信息',
|
||||
'common.required': '必填',
|
||||
'common.optional': '可选',
|
||||
'common.loading': '加载中...',
|
||||
'common.search': '搜索',
|
||||
'common.filter': '筛选',
|
||||
'common.sort': '排序',
|
||||
'common.view': '查看',
|
||||
'common.edit': '编辑',
|
||||
'common.update': '更新',
|
||||
'common.create': '创建',
|
||||
'common.remove': '移除',
|
||||
'common.refresh': '刷新',
|
||||
'common.retry': '重试',
|
||||
'common.continue': '继续',
|
||||
'common.skip': '跳过',
|
||||
'common.start': '开始',
|
||||
'common.stop': '停止',
|
||||
'common.pause': '暂停',
|
||||
'common.resume': '恢复',
|
||||
'common.reset': '重置',
|
||||
'common.clear': '清除',
|
||||
'common.select': '选择',
|
||||
'common.deselect': '取消选择',
|
||||
'common.all': '全部',
|
||||
'common.none': '无',
|
||||
'common.show': '显示',
|
||||
'common.hide': '隐藏',
|
||||
'common.open': '打开',
|
||||
'common.close': '关闭',
|
||||
'common.expand': '展开',
|
||||
'common.collapse': '折叠',
|
||||
'common.enable': '启用',
|
||||
'common.disable': '禁用',
|
||||
'common.on': '开',
|
||||
'common.off': '关',
|
||||
'common.language': '语言',
|
||||
'common.english': 'English',
|
||||
'common.chinese': '中文',
|
||||
'common.switchLanguage': '切换语言'
|
||||
}
|
||||
};
|
||||
|
||||
const t = (key: string): string => {
|
||||
return translations[language][key as keyof typeof translations['en']] || key;
|
||||
};
|
||||
|
||||
return (
|
||||
<LanguageContext.Provider value={{ language, setLanguage, t }}>
|
||||
{children}
|
||||
</LanguageContext.Provider>
|
||||
);
|
||||
};
|
||||
29
src/hooks/useTheme.ts
Normal file
29
src/hooks/useTheme.ts
Normal file
@ -0,0 +1,29 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
|
||||
type Theme = 'light' | 'dark';
|
||||
|
||||
export function useTheme() {
|
||||
const [theme, setTheme] = useState<Theme>(() => {
|
||||
const savedTheme = localStorage.getItem('theme') as Theme;
|
||||
if (savedTheme) {
|
||||
return savedTheme;
|
||||
}
|
||||
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
document.documentElement.classList.remove('light', 'dark');
|
||||
document.documentElement.classList.add(theme);
|
||||
localStorage.setItem('theme', theme);
|
||||
}, [theme]);
|
||||
|
||||
const toggleTheme = () => {
|
||||
setTheme(prevTheme => prevTheme === 'light' ? 'dark' : 'light');
|
||||
};
|
||||
|
||||
return {
|
||||
theme,
|
||||
toggleTheme,
|
||||
isDark: theme === 'dark'
|
||||
};
|
||||
}
|
||||
14
src/index.css
Normal file
14
src/index.css
Normal file
@ -0,0 +1,14 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
:root {
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji";
|
||||
line-height: 1.5;
|
||||
font-weight: 400;
|
||||
|
||||
font-synthesis: none;
|
||||
text-rendering: optimizeLegibility;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
6
src/lib/utils.ts
Normal file
6
src/lib/utils.ts
Normal file
@ -0,0 +1,6 @@
|
||||
import { clsx, type ClassValue } from "clsx"
|
||||
import { twMerge } from "tailwind-merge"
|
||||
|
||||
export function cn(...inputs: ClassValue[]) {
|
||||
return twMerge(clsx(inputs))
|
||||
}
|
||||
10
src/main.tsx
Normal file
10
src/main.tsx
Normal file
@ -0,0 +1,10 @@
|
||||
import { StrictMode } from 'react'
|
||||
import { createRoot } from 'react-dom/client'
|
||||
import App from './App'
|
||||
import './index.css'
|
||||
|
||||
createRoot(document.getElementById('root')!).render(
|
||||
<StrictMode>
|
||||
<App />
|
||||
</StrictMode>,
|
||||
)
|
||||
190
src/pages/About.tsx
Normal file
190
src/pages/About.tsx
Normal file
@ -0,0 +1,190 @@
|
||||
import { motion } from 'framer-motion';
|
||||
import { Shield, Globe, Users, Award, MapPin, Building } from 'lucide-react';
|
||||
import { useLanguage } from '@/contexts/LanguageContext';
|
||||
|
||||
export default function About() {
|
||||
const { t } = useLanguage();
|
||||
|
||||
const stats = [
|
||||
{ label: t('about.stats.experience'), value: "5+" },
|
||||
{ label: t('about.stats.users'), value: "10M+" },
|
||||
{ label: t('about.stats.servers'), value: "105+" },
|
||||
{ label: t('about.stats.countries'), value: "150+" }
|
||||
];
|
||||
|
||||
const values = [
|
||||
{
|
||||
icon: Shield,
|
||||
title: t('about.values.security'),
|
||||
description: t('about.values.security.desc')
|
||||
},
|
||||
{
|
||||
icon: Globe,
|
||||
title: t('about.values.global'),
|
||||
description: t('about.values.global.desc')
|
||||
},
|
||||
{
|
||||
icon: Users,
|
||||
title: t('about.values.customer'),
|
||||
description: t('about.values.customer.desc')
|
||||
},
|
||||
{
|
||||
icon: Award,
|
||||
title: t('about.values.excellence'),
|
||||
description: t('about.values.excellence.desc')
|
||||
}
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="min-h-screen py-20 px-4 sm:px-6 lg:px-8">
|
||||
<div className="max-w-7xl mx-auto">
|
||||
{/* Hero Section */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.8 }}
|
||||
className="text-center mb-16"
|
||||
>
|
||||
<h1 className="text-4xl md:text-5xl font-bold text-white mb-6">
|
||||
{t('about.title')} <span className="text-blue-400">{t('about.subtitle')}</span>
|
||||
</h1>
|
||||
<p className="text-xl text-gray-300 max-w-3xl mx-auto">
|
||||
{t('about.description')}
|
||||
</p>
|
||||
</motion.div>
|
||||
|
||||
{/* Company Information */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.8 }}
|
||||
viewport={{ once: true }}
|
||||
className="bg-white/10 backdrop-blur-md rounded-2xl p-8 mb-16 border border-white/20"
|
||||
>
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-8 items-center">
|
||||
<div>
|
||||
<h2 className="text-3xl font-bold text-white mb-6">{t('about.company.title')}</h2>
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-start space-x-3">
|
||||
<Building className="h-6 w-6 text-blue-400 mt-1 flex-shrink-0" />
|
||||
<div>
|
||||
<h3 className="text-lg font-semibold text-white">{t('about.company.name')}</h3>
|
||||
<p className="text-gray-300">TAW TRADERS SDN. BHD.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-start space-x-3">
|
||||
<MapPin className="h-6 w-6 text-blue-400 mt-1 flex-shrink-0" />
|
||||
<div>
|
||||
<h3 className="text-lg font-semibold text-white">{t('about.company.location')}</h3>
|
||||
<p className="text-gray-300">Malaysia</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-start space-x-3">
|
||||
<Shield className="h-6 w-6 text-blue-400 mt-1 flex-shrink-0" />
|
||||
<div>
|
||||
<h3 className="text-lg font-semibold text-white">{t('about.company.focus')}</h3>
|
||||
<p className="text-gray-300">Network Communications & VPN Services</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="bg-gradient-to-br from-blue-600 to-indigo-600 rounded-xl p-6">
|
||||
<h3 className="text-xl font-bold text-white mb-4">{t('about.mission.title')}</h3>
|
||||
<p className="text-blue-100">
|
||||
{t('about.mission.text')}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
|
||||
{/* Stats Section */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.8 }}
|
||||
viewport={{ once: true }}
|
||||
className="mb-16"
|
||||
>
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-8">
|
||||
{stats.map((stat, index) => (
|
||||
<motion.div
|
||||
key={stat.label}
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.5, delay: index * 0.1 }}
|
||||
viewport={{ once: true }}
|
||||
className="text-center bg-white/10 backdrop-blur-md rounded-xl p-6 border border-white/20"
|
||||
>
|
||||
<div className="text-3xl md:text-4xl font-bold text-blue-400 mb-2">{stat.value}</div>
|
||||
<div className="text-gray-300">{stat.label}</div>
|
||||
</motion.div>
|
||||
))}
|
||||
</div>
|
||||
</motion.div>
|
||||
|
||||
{/* Values Section */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.8 }}
|
||||
viewport={{ once: true }}
|
||||
className="mb-16"
|
||||
>
|
||||
<h2 className="text-3xl font-bold text-white text-center mb-12">{t('about.values.title')}</h2>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-8">
|
||||
{values.map((value, index) => (
|
||||
<motion.div
|
||||
key={value.title}
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.5, delay: index * 0.1 }}
|
||||
viewport={{ once: true }}
|
||||
className="bg-white/10 backdrop-blur-md rounded-xl p-6 border border-white/20 hover:border-blue-400/50 transition-colors"
|
||||
>
|
||||
<div className="flex items-start space-x-4">
|
||||
<div className="bg-blue-600/20 rounded-lg p-3 flex-shrink-0">
|
||||
<value.icon className="h-6 w-6 text-blue-400" />
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="text-xl font-semibold text-white mb-2">{value.title}</h3>
|
||||
<p className="text-gray-300">{value.description}</p>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
))}
|
||||
</div>
|
||||
</motion.div>
|
||||
|
||||
{/* Legal Information */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.8 }}
|
||||
viewport={{ once: true }}
|
||||
className="bg-gradient-to-r from-blue-600 to-indigo-600 rounded-2xl p-8"
|
||||
>
|
||||
<h2 className="text-2xl font-bold text-white mb-6">{t('about.legal.title')}</h2>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-8">
|
||||
<div>
|
||||
<h3 className="text-lg font-semibold text-blue-100 mb-3">{t('about.legal.business')}</h3>
|
||||
<p className="text-blue-100 mb-4">
|
||||
{t('about.legal.business.desc')}
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="text-lg font-semibold text-blue-100 mb-3">{t('about.legal.compliance')}</h3>
|
||||
<p className="text-blue-100">
|
||||
{t('about.legal.compliance.desc')}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="border-t border-blue-500/30 mt-6 pt-6">
|
||||
<p className="text-blue-100 text-center">
|
||||
<strong>Operated by TAW TRADERS SDN. BHD.</strong> - Your trusted partner in digital privacy and security.
|
||||
</p>
|
||||
</div>
|
||||
</motion.div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
283
src/pages/Contact.tsx
Normal file
283
src/pages/Contact.tsx
Normal file
@ -0,0 +1,283 @@
|
||||
import { motion } from 'framer-motion';
|
||||
import { useState } from 'react';
|
||||
import { Mail, Phone, MapPin, Clock, Send, User, MessageSquare } from 'lucide-react';
|
||||
import { useLanguage } from '../contexts/LanguageContext';
|
||||
|
||||
export default function Contact() {
|
||||
const { t } = useLanguage();
|
||||
const [formData, setFormData] = useState({
|
||||
name: '',
|
||||
email: '',
|
||||
subject: '',
|
||||
message: ''
|
||||
});
|
||||
|
||||
const handleSubmit = (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
// Handle form submission
|
||||
console.log('Form submitted:', formData);
|
||||
alert(t('contact.form.successMessage'));
|
||||
setFormData({ name: '', email: '', subject: '', message: '' });
|
||||
};
|
||||
|
||||
const handleChange = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
|
||||
setFormData({
|
||||
...formData,
|
||||
[e.target.name]: e.target.value
|
||||
});
|
||||
};
|
||||
|
||||
const contactInfo = [
|
||||
{
|
||||
icon: Mail,
|
||||
title: t('contact.info.email.title'),
|
||||
value: t('contact.info.email.value'),
|
||||
description: t('contact.info.email.description')
|
||||
},
|
||||
{
|
||||
icon: Clock,
|
||||
title: t('contact.info.hours.title'),
|
||||
value: t('contact.info.hours.value'),
|
||||
description: t('contact.info.hours.description')
|
||||
},
|
||||
{
|
||||
icon: MapPin,
|
||||
title: t('contact.info.address.title'),
|
||||
value: t('contact.info.address.value'),
|
||||
description: t('contact.info.address.description')
|
||||
}
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="min-h-screen py-20 px-4 sm:px-6 lg:px-8">
|
||||
<div className="max-w-7xl mx-auto">
|
||||
{/* Header */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.8 }}
|
||||
className="text-center mb-16"
|
||||
>
|
||||
<h1 className="text-4xl md:text-5xl font-bold text-white mb-6">
|
||||
{t('contact.title')} <span className="text-blue-400">{t('contact.titleHighlight')}</span>
|
||||
</h1>
|
||||
<p className="text-xl text-gray-300 max-w-3xl mx-auto">
|
||||
{t('contact.subtitle')}
|
||||
</p>
|
||||
</motion.div>
|
||||
|
||||
{/* Contact Information */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-8 mb-16">
|
||||
{contactInfo.map((info, index) => (
|
||||
<motion.div
|
||||
key={info.title}
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.5, delay: index * 0.1 }}
|
||||
viewport={{ once: true }}
|
||||
className="bg-white/10 backdrop-blur-md rounded-xl p-8 border border-white/20 text-center hover:border-blue-400/50 transition-colors"
|
||||
>
|
||||
<div className="bg-blue-600/20 rounded-lg p-4 w-fit mx-auto mb-6">
|
||||
<info.icon className="h-8 w-8 text-blue-400" />
|
||||
</div>
|
||||
<h3 className="text-xl font-semibold text-white mb-3">{info.title}</h3>
|
||||
<p className="text-blue-400 font-semibold mb-2">{info.value}</p>
|
||||
<p className="text-gray-300 text-sm">{info.description}</p>
|
||||
</motion.div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Contact Form and Company Info */}
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-12">
|
||||
{/* Contact Form */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.8 }}
|
||||
viewport={{ once: true }}
|
||||
>
|
||||
<div className="bg-white/10 backdrop-blur-md rounded-xl p-8 border border-white/20">
|
||||
<h2 className="text-2xl font-bold text-white mb-6">{t('contact.form.title')}</h2>
|
||||
<form onSubmit={handleSubmit} className="space-y-6">
|
||||
<div>
|
||||
<label htmlFor="name" className="block text-white font-semibold mb-2">
|
||||
{t('contact.form.nameLabel')}
|
||||
</label>
|
||||
<div className="relative">
|
||||
<User className="absolute left-3 top-3 h-5 w-5 text-gray-400" />
|
||||
<input
|
||||
type="text"
|
||||
id="name"
|
||||
name="name"
|
||||
value={formData.name}
|
||||
onChange={handleChange}
|
||||
required
|
||||
className="w-full pl-10 pr-4 py-3 bg-white/10 border border-white/20 rounded-lg text-white placeholder-gray-400 focus:outline-none focus:border-blue-400 transition-colors"
|
||||
placeholder={t('contact.form.namePlaceholder')}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label htmlFor="email" className="block text-white font-semibold mb-2">
|
||||
{t('contact.form.emailLabel')}
|
||||
</label>
|
||||
<div className="relative">
|
||||
<Mail className="absolute left-3 top-3 h-5 w-5 text-gray-400" />
|
||||
<input
|
||||
type="email"
|
||||
id="email"
|
||||
name="email"
|
||||
value={formData.email}
|
||||
onChange={handleChange}
|
||||
required
|
||||
className="w-full pl-10 pr-4 py-3 bg-white/10 border border-white/20 rounded-lg text-white placeholder-gray-400 focus:outline-none focus:border-blue-400 transition-colors"
|
||||
placeholder={t('contact.form.emailPlaceholder')}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label htmlFor="subject" className="block text-white font-semibold mb-2">
|
||||
{t('contact.form.subjectLabel')}
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
id="subject"
|
||||
name="subject"
|
||||
value={formData.subject}
|
||||
onChange={handleChange}
|
||||
required
|
||||
className="w-full px-4 py-3 bg-white/10 border border-white/20 rounded-lg text-white placeholder-gray-400 focus:outline-none focus:border-blue-400 transition-colors"
|
||||
placeholder={t('contact.form.subjectPlaceholder')}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label htmlFor="message" className="block text-white font-semibold mb-2">
|
||||
{t('contact.form.messageLabel')}
|
||||
</label>
|
||||
<div className="relative">
|
||||
<MessageSquare className="absolute left-3 top-3 h-5 w-5 text-gray-400" />
|
||||
<textarea
|
||||
id="message"
|
||||
name="message"
|
||||
value={formData.message}
|
||||
onChange={handleChange}
|
||||
required
|
||||
rows={5}
|
||||
className="w-full pl-10 pr-4 py-3 bg-white/10 border border-white/20 rounded-lg text-white placeholder-gray-400 focus:outline-none focus:border-blue-400 transition-colors resize-none"
|
||||
placeholder={t('contact.form.messagePlaceholder')}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<motion.button
|
||||
whileHover={{ scale: 1.02 }}
|
||||
whileTap={{ scale: 0.98 }}
|
||||
type="submit"
|
||||
className="w-full bg-blue-600 hover:bg-blue-700 text-white py-3 px-6 rounded-lg font-semibold flex items-center justify-center space-x-2 transition-colors"
|
||||
>
|
||||
<Send className="h-5 w-5" />
|
||||
<span>{t('contact.form.sendButton')}</span>
|
||||
</motion.button>
|
||||
</form>
|
||||
</div>
|
||||
</motion.div>
|
||||
|
||||
{/* Company Information */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.8, delay: 0.2 }}
|
||||
viewport={{ once: true }}
|
||||
className="space-y-8"
|
||||
>
|
||||
{/* Company Details */}
|
||||
<div className="bg-gradient-to-br from-blue-600 to-indigo-600 rounded-xl p-8">
|
||||
<h2 className="text-2xl font-bold text-white mb-6">{t('contact.companyInfo.title')}</h2>
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-start space-x-3">
|
||||
<div className="bg-white/20 rounded-lg p-2">
|
||||
<MapPin className="h-5 w-5 text-white" />
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="text-white font-semibold">{t('contact.companyInfo.registeredCompany')}</h3>
|
||||
<p className="text-blue-100">{t('company.name')}</p>
|
||||
<p className="text-blue-100 text-sm">{t('contact.companyInfo.malaysia')}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-start space-x-3">
|
||||
<div className="bg-white/20 rounded-lg p-2">
|
||||
<Mail className="h-5 w-5 text-white" />
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="text-white font-semibold">{t('contact.companyInfo.businessEmail')}</h3>
|
||||
<p className="text-blue-100">{t('contact.companyInfo.supportEmail')}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-start space-x-3">
|
||||
<div className="bg-white/20 rounded-lg p-2">
|
||||
<Clock className="h-5 w-5 text-white" />
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="text-white font-semibold">{t('contact.companyInfo.supportHours')}</h3>
|
||||
<p className="text-blue-100">{t('contact.companyInfo.supportHoursValue')}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Quick Support */}
|
||||
<div className="bg-white/10 backdrop-blur-md rounded-xl p-8 border border-white/20">
|
||||
<h3 className="text-xl font-bold text-white mb-4">{t('contact.quickSupport.title')}</h3>
|
||||
<p className="text-gray-300 mb-6">
|
||||
{t('contact.quickSupport.description')}
|
||||
</p>
|
||||
<div className="space-y-3">
|
||||
<div className="flex items-center space-x-3 text-blue-400">
|
||||
<div className="w-2 h-2 bg-blue-400 rounded-full"></div>
|
||||
<span>{t('contact.quickSupport.topic1')}</span>
|
||||
</div>
|
||||
<div className="flex items-center space-x-3 text-blue-400">
|
||||
<div className="w-2 h-2 bg-blue-400 rounded-full"></div>
|
||||
<span>{t('contact.quickSupport.topic2')}</span>
|
||||
</div>
|
||||
<div className="flex items-center space-x-3 text-blue-400">
|
||||
<div className="w-2 h-2 bg-blue-400 rounded-full"></div>
|
||||
<span>{t('contact.quickSupport.topic3')}</span>
|
||||
</div>
|
||||
<div className="flex items-center space-x-3 text-blue-400">
|
||||
<div className="w-2 h-2 bg-blue-400 rounded-full"></div>
|
||||
<span>{t('contact.quickSupport.topic4')}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Response Time */}
|
||||
<div className="bg-white/10 backdrop-blur-md rounded-xl p-8 border border-white/20">
|
||||
<h3 className="text-xl font-bold text-white mb-4">{t('contact.responseTime.title')}</h3>
|
||||
<p className="text-gray-300 mb-4">
|
||||
{t('contact.responseTime.description')}
|
||||
</p>
|
||||
<div className="space-y-2">
|
||||
<div className="flex justify-between items-center">
|
||||
<span className="text-gray-300">{t('contact.responseTime.general')}</span>
|
||||
<span className="text-blue-400 font-semibold">{t('contact.responseTime.generalValue')}</span>
|
||||
</div>
|
||||
<div className="flex justify-between items-center">
|
||||
<span className="text-gray-300">{t('contact.responseTime.technical')}</span>
|
||||
<span className="text-blue-400 font-semibold">{t('contact.responseTime.technicalValue')}</span>
|
||||
</div>
|
||||
<div className="flex justify-between items-center">
|
||||
<span className="text-gray-300">{t('contact.responseTime.billing')}</span>
|
||||
<span className="text-blue-400 font-semibold">{t('contact.responseTime.billingValue')}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
221
src/pages/Home.tsx
Normal file
221
src/pages/Home.tsx
Normal file
@ -0,0 +1,221 @@
|
||||
import { motion } from 'framer-motion';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { Shield, Globe, Zap, Lock, Smartphone, Laptop, Download, ArrowRight, CheckCircle } from 'lucide-react';
|
||||
import { useLanguage } from '@/contexts/LanguageContext';
|
||||
|
||||
export default function Home() {
|
||||
const { t } = useLanguage();
|
||||
|
||||
const features = [
|
||||
{
|
||||
icon: Globe,
|
||||
title: t('home.features.global'),
|
||||
description: t('home.features.global.desc')
|
||||
},
|
||||
{
|
||||
icon: Zap,
|
||||
title: t('home.features.speed'),
|
||||
description: t('home.features.speed.desc')
|
||||
},
|
||||
{
|
||||
icon: Lock,
|
||||
title: t('home.features.encryption'),
|
||||
description: t('home.features.encryption.desc')
|
||||
},
|
||||
{
|
||||
icon: Shield,
|
||||
title: t('home.features.logs'),
|
||||
description: t('home.features.logs.desc')
|
||||
}
|
||||
];
|
||||
|
||||
const platforms = [
|
||||
{ name: t('common.ios'), icon: Smartphone, status: t('home.status.comingSoon') },
|
||||
{ name: t('common.android'), icon: Smartphone, status: t('home.status.available') },
|
||||
{ name: t('common.windows'), icon: Laptop, status: t('home.status.available') },
|
||||
{ name: t('common.macos'), icon: Laptop, status: t('home.status.comingSoon') }
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="min-h-screen">
|
||||
{/* Hero Section */}
|
||||
<section className="relative py-20 px-4 sm:px-6 lg:px-8">
|
||||
<div className="max-w-7xl mx-auto text-center">
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.8 }}
|
||||
>
|
||||
<h1 className="text-4xl md:text-6xl font-bold text-white mb-6">
|
||||
{t('home.hero.title')}
|
||||
<span className="text-blue-400"> {t('home.hero.subtitle')}</span>
|
||||
</h1>
|
||||
<p className="text-xl text-gray-300 mb-8 max-w-3xl mx-auto">
|
||||
{t('home.hero.description')}
|
||||
</p>
|
||||
<div className="flex flex-col sm:flex-row gap-4 justify-center">
|
||||
<motion.button
|
||||
whileHover={{ scale: 1.05 }}
|
||||
whileTap={{ scale: 0.95 }}
|
||||
className="bg-blue-600 hover:bg-blue-700 text-white px-8 py-4 rounded-lg font-semibold text-lg flex items-center space-x-2 transition-colors"
|
||||
>
|
||||
<Download className="h-5 w-5" />
|
||||
<span>{t('home.hero.cta')}</span>
|
||||
</motion.button>
|
||||
<Link
|
||||
to="/about"
|
||||
className="border border-white/30 text-white hover:bg-white/10 px-8 py-4 rounded-lg font-semibold text-lg flex items-center space-x-2 transition-colors"
|
||||
>
|
||||
<span>{t('home.hero.learnMore')}</span>
|
||||
<ArrowRight className="h-5 w-5" />
|
||||
</Link>
|
||||
</div>
|
||||
</motion.div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Features Section */}
|
||||
<section className="py-20 px-4 sm:px-6 lg:px-8 bg-white/5">
|
||||
<div className="max-w-7xl mx-auto">
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
whileInView={{ opacity: 1 }}
|
||||
transition={{ duration: 0.8 }}
|
||||
viewport={{ once: true }}
|
||||
className="text-center mb-16"
|
||||
>
|
||||
<h2 className="text-3xl md:text-4xl font-bold text-white mb-4">
|
||||
{t('home.features.title')}
|
||||
</h2>
|
||||
<p className="text-gray-300 text-lg max-w-2xl mx-auto">
|
||||
{t('home.features.subtitle')}
|
||||
</p>
|
||||
</motion.div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-8">
|
||||
{features.map((feature, index) => (
|
||||
<motion.div
|
||||
key={feature.title}
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.5, delay: index * 0.1 }}
|
||||
viewport={{ once: true }}
|
||||
className="bg-white/10 backdrop-blur-md rounded-xl p-6 border border-white/20 hover:border-blue-400/50 transition-colors"
|
||||
>
|
||||
<div className="bg-blue-600/20 rounded-lg p-3 w-fit mb-4">
|
||||
<feature.icon className="h-8 w-8 text-blue-400" />
|
||||
</div>
|
||||
<h3 className="text-xl font-semibold text-white mb-3">{feature.title}</h3>
|
||||
<p className="text-gray-300">{feature.description}</p>
|
||||
</motion.div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Platforms Section */}
|
||||
<section className="py-20 px-4 sm:px-6 lg:px-8">
|
||||
<div className="max-w-7xl mx-auto">
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
whileInView={{ opacity: 1 }}
|
||||
transition={{ duration: 0.8 }}
|
||||
viewport={{ once: true }}
|
||||
className="text-center mb-16"
|
||||
>
|
||||
<h2 className="text-3xl md:text-4xl font-bold text-white mb-4">
|
||||
{t('home.platforms.title')}
|
||||
</h2>
|
||||
<p className="text-gray-300 text-lg max-w-2xl mx-auto">
|
||||
{t('home.platforms.subtitle')}
|
||||
</p>
|
||||
</motion.div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
|
||||
{platforms.map((platform, index) => (
|
||||
<motion.div
|
||||
key={platform.name}
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.5, delay: index * 0.1 }}
|
||||
viewport={{ once: true }}
|
||||
className="bg-white/10 backdrop-blur-md rounded-xl p-6 border border-white/20 text-center hover:border-blue-400/50 transition-colors"
|
||||
>
|
||||
<div className="bg-blue-600/20 rounded-lg p-3 w-fit mx-auto mb-4">
|
||||
<platform.icon className="h-8 w-8 text-blue-400" />
|
||||
</div>
|
||||
<h3 className="text-xl font-semibold text-white mb-2">{platform.name}</h3>
|
||||
<p className={`text-sm ${platform.status === 'Available' ? 'text-green-400' : 'text-yellow-400'}`}>
|
||||
{platform.status}
|
||||
</p>
|
||||
</motion.div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* CTA Section */}
|
||||
<section className="py-20 px-4 sm:px-6 lg:px-8 bg-gradient-to-r from-blue-600 to-indigo-600">
|
||||
<div className="max-w-4xl mx-auto text-center">
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.8 }}
|
||||
viewport={{ once: true }}
|
||||
>
|
||||
<h2 className="text-3xl md:text-4xl font-bold text-white mb-6">
|
||||
{t('home.cta.title')}
|
||||
</h2>
|
||||
<p className="text-xl text-blue-100 mb-8">
|
||||
{t('home.cta.subtitle')}
|
||||
</p>
|
||||
<div className="flex flex-col sm:flex-row gap-4 justify-center items-center">
|
||||
<motion.button
|
||||
whileHover={{ scale: 1.05 }}
|
||||
whileTap={{ scale: 0.95 }}
|
||||
className="bg-white text-blue-600 hover:bg-gray-100 px-8 py-4 rounded-lg font-semibold text-lg flex items-center space-x-2 transition-colors"
|
||||
>
|
||||
<Shield className="h-5 w-5" />
|
||||
<span>{t('home.cta.button')}</span>
|
||||
</motion.button>
|
||||
<div className="flex items-center space-x-4 text-blue-100">
|
||||
<div className="flex items-center space-x-2">
|
||||
<CheckCircle className="h-5 w-5" />
|
||||
<span>{t('home.guarantee.moneyback')}</span>
|
||||
</div>
|
||||
<div className="flex items-center space-x-2">
|
||||
<CheckCircle className="h-5 w-5" />
|
||||
<span>{t('home.guarantee.free')}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Trust Indicators */}
|
||||
<section className="py-16 px-4 sm:px-6 lg:px-8 bg-white/5">
|
||||
<div className="max-w-7xl mx-auto">
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-8 text-center">
|
||||
<div>
|
||||
<div className="text-3xl font-bold text-blue-400 mb-2">10M+</div>
|
||||
<div className="text-gray-300">{t('home.stats.users')}</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-3xl font-bold text-blue-400 mb-2">105+</div>
|
||||
<div className="text-gray-300">{t('home.stats.countries')}</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-3xl font-bold text-blue-400 mb-2">99.9%</div>
|
||||
<div className="text-gray-300">{t('home.stats.uptime')}</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-3xl font-bold text-blue-400 mb-2">24/7</div>
|
||||
<div className="text-gray-300">{t('home.stats.support')}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
187
src/pages/Privacy.tsx
Normal file
187
src/pages/Privacy.tsx
Normal file
@ -0,0 +1,187 @@
|
||||
import { motion } from 'framer-motion';
|
||||
import { Shield, Calendar, User, Globe, Lock } from 'lucide-react';
|
||||
import { useLanguage } from '@/contexts/LanguageContext';
|
||||
|
||||
export default function Privacy() {
|
||||
const { t } = useLanguage();
|
||||
|
||||
const sections = [
|
||||
{
|
||||
title: "Information We Collect",
|
||||
content: [
|
||||
"Personal Information: When you create an account, we collect your email address, username, and payment information.",
|
||||
"Usage Data: We collect information about how you use our VPN services, including connection timestamps and server locations.",
|
||||
"Device Information: We may collect device identifiers, operating system, and app version information.",
|
||||
"Payment Information: Payment details are processed through secure third-party payment processors and are not stored on our servers."
|
||||
]
|
||||
},
|
||||
{
|
||||
title: "How We Use Your Information",
|
||||
content: [
|
||||
"Service Provision: To provide and maintain our VPN services, including customer support.",
|
||||
"Account Management: To manage your account, process payments, and send service-related communications.",
|
||||
"Service Improvement: To analyze usage patterns and improve our services and user experience.",
|
||||
"Legal Compliance: To comply with legal obligations and protect our legal rights."
|
||||
]
|
||||
},
|
||||
{
|
||||
title: "Data Security",
|
||||
content: [
|
||||
"Encryption: All data transmitted through our VPN services is encrypted using military-grade AES-256 encryption.",
|
||||
"No-Log Policy: We do not log your browsing activity, traffic destinations, or DNS queries while connected to our VPN.",
|
||||
"Secure Infrastructure: Our servers are maintained with the highest security standards and regular security audits.",
|
||||
"Data Retention: We retain minimal connection logs for service optimization and are automatically deleted after 30 days."
|
||||
]
|
||||
},
|
||||
{
|
||||
title: "Third-Party Services",
|
||||
content: [
|
||||
"Payment Processors: We use trusted third-party payment processors to handle payment transactions securely.",
|
||||
"Analytics: We may use analytics services to understand app usage patterns, excluding personal browsing data.",
|
||||
"Service Providers: We may engage trusted third-party service providers to assist in providing our services."
|
||||
]
|
||||
},
|
||||
{
|
||||
title: "Your Rights",
|
||||
content: [
|
||||
"Access: You have the right to access the personal information we hold about you.",
|
||||
"Correction: You can update or correct your personal information through your account settings.",
|
||||
"Deletion: You can request deletion of your account and associated personal information.",
|
||||
"Data Portability: You can request a copy of your personal data in a machine-readable format."
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="min-h-screen py-20 px-4 sm:px-6 lg:px-8">
|
||||
<div className="max-w-4xl mx-auto">
|
||||
{/* Header */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.8 }}
|
||||
className="text-center mb-12"
|
||||
>
|
||||
<div className="flex justify-center mb-6">
|
||||
<div className="bg-blue-600/20 rounded-lg p-4">
|
||||
<Shield className="h-12 w-12 text-blue-400" />
|
||||
</div>
|
||||
</div>
|
||||
<h1 className="text-4xl md:text-5xl font-bold text-white mb-6">
|
||||
{t('privacy.title')}
|
||||
</h1>
|
||||
<p className="text-xl text-gray-300 mb-4">
|
||||
{t('privacy.updated')}
|
||||
</p>
|
||||
<p className="text-gray-300 max-w-2xl mx-auto">
|
||||
{t('privacy.description')}
|
||||
</p>
|
||||
</motion.div>
|
||||
|
||||
{/* Policy Overview */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.8 }}
|
||||
viewport={{ once: true }}
|
||||
className="bg-gradient-to-r from-blue-600 to-indigo-600 rounded-2xl p-8 mb-12"
|
||||
>
|
||||
<h2 className="text-2xl font-bold text-white mb-4">{t('privacy.commitment.title')}</h2>
|
||||
<p className="text-blue-100 mb-4">
|
||||
{t('privacy.commitment.text')}
|
||||
</p>
|
||||
<div className="flex items-center space-x-4 text-blue-100">
|
||||
<Lock className="h-5 w-5" />
|
||||
<span>{t('privacy.nolog')}</span>
|
||||
</div>
|
||||
<div className="flex items-center space-x-4 text-blue-100 mt-2">
|
||||
<Globe className="h-5 w-5" />
|
||||
<span>{t('privacy.laws')}</span>
|
||||
</div>
|
||||
</motion.div>
|
||||
|
||||
{/* Policy Sections */}
|
||||
<div className="space-y-8">
|
||||
{sections.map((section, index) => (
|
||||
<motion.div
|
||||
key={section.title}
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.5, delay: index * 0.1 }}
|
||||
viewport={{ once: true }}
|
||||
className="bg-white/10 backdrop-blur-md rounded-xl p-8 border border-white/20"
|
||||
>
|
||||
<h2 className="text-2xl font-bold text-white mb-6">{section.title}</h2>
|
||||
<ul className="space-y-3">
|
||||
{section.content.map((item, itemIndex) => (
|
||||
<li key={itemIndex} className="flex items-start space-x-3">
|
||||
<div className="bg-blue-600/20 rounded-full p-1 mt-1 flex-shrink-0">
|
||||
<div className="h-2 w-2 bg-blue-400 rounded-full"></div>
|
||||
</div>
|
||||
<p className="text-gray-300">{item}</p>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</motion.div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Contact Information */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.8 }}
|
||||
viewport={{ once: true }}
|
||||
className="bg-white/10 backdrop-blur-md rounded-xl p-8 mt-12 border border-white/20"
|
||||
>
|
||||
<h2 className="text-2xl font-bold text-white mb-6">{t('privacy.contact.title')}</h2>
|
||||
<p className="text-gray-300 mb-6">
|
||||
{t('privacy.contact.text')}
|
||||
</p>
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-center space-x-3">
|
||||
<div className="bg-blue-600/20 rounded-lg p-2">
|
||||
<User className="h-5 w-5 text-blue-400" />
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="text-white font-semibold">{t('privacy.company')}</h3>
|
||||
<p className="text-gray-300">TAW TRADERS SDN. BHD.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center space-x-3">
|
||||
<div className="bg-blue-600/20 rounded-lg p-2">
|
||||
<Globe className="h-5 w-5 text-blue-400" />
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="text-white font-semibold">{t('privacy.email')}</h3>
|
||||
<p className="text-gray-300">privacy@hifastvpn.com</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center space-x-3">
|
||||
<div className="bg-blue-600/20 rounded-lg p-2">
|
||||
<Calendar className="h-5 w-5 text-blue-400" />
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="text-white font-semibold">{t('privacy.policy')}</h3>
|
||||
<p className="text-gray-300">{t('privacy.policy.text')}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
|
||||
{/* Final Note */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.8 }}
|
||||
viewport={{ once: true }}
|
||||
className="text-center mt-12 p-6 bg-gradient-to-r from-blue-600/20 to-indigo-600/20 rounded-xl border border-blue-400/30"
|
||||
>
|
||||
<p className="text-gray-300">
|
||||
{t('privacy.acknowledgement')}
|
||||
</p>
|
||||
</motion.div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
232
src/pages/Terms.tsx
Normal file
232
src/pages/Terms.tsx
Normal file
@ -0,0 +1,232 @@
|
||||
import { motion } from 'framer-motion';
|
||||
import { Shield, Calendar, User, Globe, AlertTriangle, CheckCircle } from 'lucide-react';
|
||||
import { useLanguage } from '../contexts/LanguageContext';
|
||||
|
||||
export default function Terms() {
|
||||
const { t } = useLanguage();
|
||||
const sections = [
|
||||
{
|
||||
title: t('terms.acceptance.title'),
|
||||
content: [
|
||||
t('terms.acceptance.content1'),
|
||||
t('terms.acceptance.content2'),
|
||||
t('terms.acceptance.content3')
|
||||
]
|
||||
},
|
||||
{
|
||||
title: t('terms.service.title'),
|
||||
content: [
|
||||
t('terms.service.content1'),
|
||||
t('terms.service.content2'),
|
||||
t('terms.service.content3')
|
||||
]
|
||||
},
|
||||
{
|
||||
title: t('terms.responsibilities.title'),
|
||||
content: [
|
||||
t('terms.responsibilities.content1'),
|
||||
t('terms.responsibilities.content2'),
|
||||
t('terms.responsibilities.content3'),
|
||||
t('terms.responsibilities.content4')
|
||||
]
|
||||
},
|
||||
{
|
||||
title: t('terms.prohibited.title'),
|
||||
content: [
|
||||
t('terms.prohibited.content1'),
|
||||
t('terms.prohibited.content2'),
|
||||
t('terms.prohibited.content3'),
|
||||
t('terms.prohibited.content4')
|
||||
]
|
||||
},
|
||||
{
|
||||
title: t('terms.limitations.title'),
|
||||
content: [
|
||||
t('terms.limitations.content1'),
|
||||
t('terms.limitations.content2'),
|
||||
t('terms.limitations.content3'),
|
||||
t('terms.limitations.content4')
|
||||
]
|
||||
},
|
||||
{
|
||||
title: t('terms.payment.title'),
|
||||
content: [
|
||||
t('terms.payment.content1'),
|
||||
t('terms.payment.content2'),
|
||||
t('terms.payment.content3'),
|
||||
t('terms.payment.content4')
|
||||
]
|
||||
},
|
||||
{
|
||||
title: t('terms.privacy.title'),
|
||||
content: [
|
||||
t('terms.privacy.content1'),
|
||||
t('terms.privacy.content2'),
|
||||
t('terms.privacy.content3'),
|
||||
t('terms.privacy.content4')
|
||||
]
|
||||
},
|
||||
{
|
||||
title: t('terms.termination.title'),
|
||||
content: [
|
||||
t('terms.termination.content1'),
|
||||
t('terms.termination.content2'),
|
||||
t('terms.termination.content3'),
|
||||
t('terms.termination.content4')
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="min-h-screen py-20 px-4 sm:px-6 lg:px-8">
|
||||
<div className="max-w-4xl mx-auto">
|
||||
{/* Header */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.8 }}
|
||||
className="text-center mb-12"
|
||||
>
|
||||
<div className="flex justify-center mb-6">
|
||||
<div className="bg-blue-600/20 rounded-lg p-4">
|
||||
<Shield className="h-12 w-12 text-blue-400" />
|
||||
</div>
|
||||
</div>
|
||||
<h1 className="text-4xl md:text-5xl font-bold text-white mb-6">
|
||||
{t('terms.title')}
|
||||
</h1>
|
||||
<p className="text-xl text-gray-300 mb-4">
|
||||
{t('terms.lastUpdated')}: {t('terms.lastUpdatedDate')}
|
||||
</p>
|
||||
<p className="text-gray-300 max-w-2xl mx-auto">
|
||||
{t('terms.subtitle')}
|
||||
</p>
|
||||
</motion.div>
|
||||
|
||||
{/* Important Notice */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.8 }}
|
||||
viewport={{ once: true }}
|
||||
className="bg-yellow-600/20 border border-yellow-400/30 rounded-xl p-6 mb-12"
|
||||
>
|
||||
<div className="flex items-start space-x-3">
|
||||
<AlertTriangle className="h-6 w-6 text-yellow-400 flex-shrink-0 mt-1" />
|
||||
<div>
|
||||
<h3 className="text-lg font-semibold text-yellow-400 mb-2">{t('terms.importantNotice.title')}</h3>
|
||||
<p className="text-yellow-100">
|
||||
{t('terms.importantNotice.content')}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
|
||||
{/* Terms Sections */}
|
||||
<div className="space-y-8">
|
||||
{sections.map((section, index) => (
|
||||
<motion.div
|
||||
key={section.title}
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.5, delay: index * 0.1 }}
|
||||
viewport={{ once: true }}
|
||||
className="bg-white/10 backdrop-blur-md rounded-xl p-8 border border-white/20"
|
||||
>
|
||||
<h2 className="text-2xl font-bold text-white mb-6">{section.title}</h2>
|
||||
<ul className="space-y-3">
|
||||
{section.content.map((item, itemIndex) => (
|
||||
<li key={itemIndex} className="flex items-start space-x-3">
|
||||
<div className="bg-blue-600/20 rounded-full p-1 mt-1 flex-shrink-0">
|
||||
<div className="h-2 w-2 bg-blue-400 rounded-full"></div>
|
||||
</div>
|
||||
<p className="text-gray-300">{item}</p>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</motion.div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Final Provisions */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.8 }}
|
||||
viewport={{ once: true }}
|
||||
className="bg-white/10 backdrop-blur-md rounded-xl p-8 mt-12 border border-white/20"
|
||||
>
|
||||
<h2 className="text-2xl font-bold text-white mb-6">{t('terms.finalProvisions.title')}</h2>
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-start space-x-3">
|
||||
<CheckCircle className="h-5 w-5 text-blue-400 mt-1 flex-shrink-0" />
|
||||
<p className="text-gray-300">
|
||||
<strong>{t('terms.finalProvisions.governingLaw')}:</strong> {t('terms.finalProvisions.governingLawContent')}
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex items-start space-x-3">
|
||||
<CheckCircle className="h-5 w-5 text-blue-400 mt-1 flex-shrink-0" />
|
||||
<p className="text-gray-300">
|
||||
<strong>{t('terms.finalProvisions.changesToTerms')}:</strong> {t('terms.finalProvisions.changesToTermsContent')}
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex items-start space-x-3">
|
||||
<CheckCircle className="h-5 w-5 text-blue-400 mt-1 flex-shrink-0" />
|
||||
<p className="text-gray-300">
|
||||
<strong>{t('terms.finalProvisions.contact')}:</strong> {t('terms.finalProvisions.contactContent')}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
|
||||
{/* Company Information */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.8 }}
|
||||
viewport={{ once: true }}
|
||||
className="bg-gradient-to-r from-blue-600 to-indigo-600 rounded-2xl p-8 mt-12"
|
||||
>
|
||||
<h2 className="text-2xl font-bold text-white mb-6">{t('terms.serviceProvider.title')}</h2>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-8">
|
||||
<div>
|
||||
<h3 className="text-lg font-semibold text-blue-100 mb-3">{t('terms.serviceProvider.companyDetails')}</h3>
|
||||
<div className="space-y-2 text-blue-100">
|
||||
<p><strong>{t('terms.serviceProvider.companyName')}:</strong> {t('company.name')}</p>
|
||||
<p><strong>{t('terms.serviceProvider.businessType')}:</strong> {t('terms.serviceProvider.businessTypeValue')}</p>
|
||||
<p><strong>{t('terms.serviceProvider.registration')}:</strong> {t('terms.serviceProvider.registrationValue')}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="text-lg font-semibold text-blue-100 mb-3">{t('terms.serviceProvider.serviceCommitment')}</h3>
|
||||
<p className="text-blue-100">
|
||||
{t('terms.serviceProvider.serviceCommitmentContent')}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="border-t border-blue-500/30 mt-6 pt-6">
|
||||
<p className="text-blue-100 text-center">
|
||||
<strong>{t('terms.serviceProvider.operatedBy')}</strong> - {t('terms.serviceProvider.trustedProvider')}
|
||||
</p>
|
||||
</div>
|
||||
</motion.div>
|
||||
|
||||
{/* Acceptance */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.8 }}
|
||||
viewport={{ once: true }}
|
||||
className="text-center mt-12 p-6 bg-gradient-to-r from-blue-600/20 to-indigo-600/20 rounded-xl border border-blue-400/30"
|
||||
>
|
||||
<p className="text-gray-300 mb-4">
|
||||
{t('terms.acceptance.acknowledgment')}
|
||||
</p>
|
||||
<p className="text-blue-400 font-semibold">
|
||||
{t('terms.acceptance.copyright')}
|
||||
</p>
|
||||
</motion.div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
1
src/vite-env.d.ts
vendored
Normal file
1
src/vite-env.d.ts
vendored
Normal file
@ -0,0 +1 @@
|
||||
/// <reference types="vite/client" />
|
||||
13
tailwind.config.js
Normal file
13
tailwind.config.js
Normal file
@ -0,0 +1,13 @@
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
|
||||
export default {
|
||||
darkMode: "class",
|
||||
content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"],
|
||||
theme: {
|
||||
container: {
|
||||
center: true,
|
||||
},
|
||||
extend: {},
|
||||
},
|
||||
plugins: [],
|
||||
};
|
||||
36
tsconfig.json
Normal file
36
tsconfig.json
Normal file
@ -0,0 +1,36 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
|
||||
"target": "ES2020",
|
||||
"useDefineForClassFields": true,
|
||||
"lib": [
|
||||
"ES2020",
|
||||
"DOM",
|
||||
"DOM.Iterable"
|
||||
],
|
||||
"module": "ESNext",
|
||||
"skipLibCheck": true,
|
||||
"moduleResolution": "bundler",
|
||||
"allowImportingTsExtensions": true,
|
||||
"verbatimModuleSyntax": false,
|
||||
"moduleDetection": "force",
|
||||
"noEmit": true,
|
||||
"jsx": "react-jsx",
|
||||
"strict": false,
|
||||
"noUnusedLocals": false,
|
||||
"noUnusedParameters": false,
|
||||
"noFallthroughCasesInSwitch": false,
|
||||
"noUncheckedSideEffectImports": false,
|
||||
"forceConsistentCasingInFileNames": false,
|
||||
"baseUrl": "./",
|
||||
"paths": {
|
||||
"@/*": [
|
||||
"./src/*"
|
||||
]
|
||||
}
|
||||
},
|
||||
"include": [
|
||||
"src",
|
||||
"api"
|
||||
]
|
||||
}
|
||||
30
vite.config.ts
Normal file
30
vite.config.ts
Normal file
@ -0,0 +1,30 @@
|
||||
import { defineConfig } from 'vite'
|
||||
import react from '@vitejs/plugin-react'
|
||||
import tsconfigPaths from "vite-tsconfig-paths";
|
||||
import { traeBadgePlugin } from 'vite-plugin-trae-solo-badge';
|
||||
|
||||
// https://vite.dev/config/
|
||||
export default defineConfig({
|
||||
build: {
|
||||
sourcemap: 'hidden',
|
||||
},
|
||||
plugins: [
|
||||
react({
|
||||
babel: {
|
||||
plugins: [
|
||||
'react-dev-locator',
|
||||
],
|
||||
},
|
||||
}),
|
||||
traeBadgePlugin({
|
||||
variant: 'dark',
|
||||
position: 'bottom-right',
|
||||
prodOnly: true,
|
||||
clickable: true,
|
||||
clickUrl: 'https://www.trae.ai/solo?showJoin=1',
|
||||
autoTheme: true,
|
||||
autoThemeTarget: '#root'
|
||||
}),
|
||||
tsconfigPaths()
|
||||
],
|
||||
})
|
||||
Loading…
x
Reference in New Issue
Block a user