From 7f1f70ee0c81c8e2b8789fedbd3deee1db28412f Mon Sep 17 00:00:00 2001 From: TinsFox Date: Sat, 2 Nov 2024 16:15:57 +0800 Subject: [PATCH] feat: use shadcn sidebar component (#14) * feat: use new sidebar * fix: layout overflow * fix: remove warp * fix: login page i18n text * fix: content scroll * docs: update doc * feat: header backdrop blur * refactor: refactor language switch use select --- .vscode/settings.json | 9 +- components.json | 2 +- .../en/guide/what-is-shadcn-ui-boilerplate.md | 16 - .../zh/guide/what-is-shadcn-ui-boilerplate.md | 18 - locales/en/auth.json | 37 + locales/en/common.json | 24 + locales/en/errors.json | 14 + locales/en/forms.json | 55 ++ locales/en/navigation.json | 39 + locales/en/settings.json | 119 +++ locales/zh-CN/auth.json | 37 + locales/zh-CN/common.json | 24 + locales/zh-CN/errors.json | 14 + locales/zh-CN/forms.json | 55 ++ locales/zh-CN/navigation.json | 39 + locales/zh-CN/settings.json | 124 +++ src/components/app-sidebar.tsx | 67 ++ src/components/fallback.tsx | 6 +- src/components/language-switch.tsx | 6 +- src/components/layout/header.tsx | 42 - src/components/layout/page-container.tsx | 34 - .../layout/sidebar/account-switcher.tsx | 57 -- src/components/layout/sidebar/data.tsx | 16 +- src/components/layout/sidebar/index.tsx | 150 ---- src/components/layout/sidebar/mobile-nav.tsx | 27 - src/components/nav-breadcrumb.tsx | 97 +++ src/components/nav-main.tsx | 92 +++ src/components/nav-projects.tsx | 87 ++ src/components/nav-secondary.tsx | 40 + src/components/nav-user.tsx | 109 +++ src/components/ui/breadcrumb.tsx | 14 +- src/components/ui/sidebar.tsx | 764 ++++++++++++++++++ src/global.d.ts | 9 +- src/hooks/use-mobile.tsx | 19 + src/i18n/index.ts | 62 +- src/i18n/locales/en.json | 269 ------ src/i18n/locales/zh-CN.json | 264 ------ src/models/menu.ts | 10 +- src/pages/(admin)/(with-layout)/layout.tsx | 11 - .../(admin)/(with-layout)/list/basic-list.tsx | 2 +- .../(with-layout)/settings/account.tsx | 6 +- .../(with-layout)/settings/appearance.tsx | 6 +- .../settings/components/account-form.tsx | 130 +-- .../settings/components/appearance-form.tsx | 23 +- .../settings/components/display-form.tsx | 23 +- .../components/notifications-form.tsx | 39 +- .../settings/components/profile-form.tsx | 37 +- .../(with-layout)/settings/display.tsx | 6 +- .../(admin)/(with-layout)/settings/layout.tsx | 14 +- .../(with-layout)/settings/notifications.tsx | 6 +- .../(with-layout)/settings/profile.tsx | 6 +- src/pages/(admin)/layout.tsx | 94 +-- src/pages/(external)/login/index.tsx | 14 +- src/providers/root-providers.tsx | 1 + src/styles/index.css | 16 + src/types/i18next.d.ts | 14 + tailwind.config.ts | 64 +- tsconfig.app.json | 1 - 58 files changed, 2180 insertions(+), 1200 deletions(-) create mode 100644 locales/en/auth.json create mode 100644 locales/en/common.json create mode 100644 locales/en/errors.json create mode 100644 locales/en/forms.json create mode 100644 locales/en/navigation.json create mode 100644 locales/en/settings.json create mode 100644 locales/zh-CN/auth.json create mode 100644 locales/zh-CN/common.json create mode 100644 locales/zh-CN/errors.json create mode 100644 locales/zh-CN/forms.json create mode 100644 locales/zh-CN/navigation.json create mode 100644 locales/zh-CN/settings.json create mode 100644 src/components/app-sidebar.tsx delete mode 100644 src/components/layout/header.tsx delete mode 100644 src/components/layout/page-container.tsx delete mode 100644 src/components/layout/sidebar/account-switcher.tsx delete mode 100644 src/components/layout/sidebar/index.tsx delete mode 100644 src/components/layout/sidebar/mobile-nav.tsx create mode 100644 src/components/nav-breadcrumb.tsx create mode 100644 src/components/nav-main.tsx create mode 100644 src/components/nav-projects.tsx create mode 100644 src/components/nav-secondary.tsx create mode 100644 src/components/nav-user.tsx create mode 100644 src/components/ui/sidebar.tsx create mode 100644 src/hooks/use-mobile.tsx delete mode 100644 src/i18n/locales/en.json delete mode 100644 src/i18n/locales/zh-CN.json delete mode 100644 src/pages/(admin)/(with-layout)/layout.tsx create mode 100644 src/types/i18next.d.ts diff --git a/.vscode/settings.json b/.vscode/settings.json index 861b841..bfcdd11 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -49,8 +49,9 @@ ["cva\\(([^)]*)\\)", "[\"'`]([^\"'`]*).*?[\"'`]"], ["cn\\(([^)]*)\\)", "[\"'`]([^\"'`]*).*?[\"'`]"] ], - "i18n-ally.localesPaths": ["src/i18n", "src/i18n/locales"], - "i18n-ally.sourceLanguage": "en", - "i18n-ally.enabledFrameworks": ["i18next", "react"], - "i18n-ally.keystyle": "nested" + "i18n-ally.pathMatcher": "{locale}/{namespaces}.json", + "i18n-ally.namespace": true, + "i18n-ally.keystyle": "nested", + "i18n-ally.localesPaths": "locales", + "i18n-ally.displayLanguage": "zh-CN" } diff --git a/components.json b/components.json index 4dadb5a..680c2ac 100644 --- a/components.json +++ b/components.json @@ -4,7 +4,7 @@ "rsc": false, "tsx": true, "tailwind": { - "config": "tailwind.config.js", + "config": "tailwind.config.ts", "css": "src/styles/index.css", "baseColor": "slate", "cssVariables": true, diff --git a/docs/en/guide/what-is-shadcn-ui-boilerplate.md b/docs/en/guide/what-is-shadcn-ui-boilerplate.md index 7430b91..558b3b2 100644 --- a/docs/en/guide/what-is-shadcn-ui-boilerplate.md +++ b/docs/en/guide/what-is-shadcn-ui-boilerplate.md @@ -26,19 +26,3 @@ If you have any questions or suggestions, please submit an issue or pr. ## Donate If you find this project helpful, please consider giving us a star ⭐️ - -or 👇 - -[![TinsFox's Profile](https://afdian-connect-nine.vercel.app/profile.svg)](https://afdian.com/a/tinsfox) - -[!["Buy Me A Coffee"](https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png)](https://www.buymeacoffee.com/TinsFox) - -微信赞赏码 - -## Join our WeChat group - -

- 微信群二维码 -

- -Scan the above QR code to join our WeChat group, and discuss with other developers! diff --git a/docs/zh/guide/what-is-shadcn-ui-boilerplate.md b/docs/zh/guide/what-is-shadcn-ui-boilerplate.md index 9e397de..0988d40 100644 --- a/docs/zh/guide/what-is-shadcn-ui-boilerplate.md +++ b/docs/zh/guide/what-is-shadcn-ui-boilerplate.md @@ -29,21 +29,3 @@ shadcn/ui boilerplate 是一个基于 [**shadcn/ui**](https://github.com/shadcn- ## 赞赏 如果你觉得这个项目对你有帮助,欢迎给我们一个 star ⭐️ - -或者 👇 - - -[!["Buy Me A Coffee"](https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png)](https://www.buymeacoffee.com/TinsFox) - -[![TinsFox's Profile](https://afdian-connect-nine.vercel.app/profile.svg)](https://afdian.com/a/tinsfox) - -微信赞赏码 - -## 加入我们的微信群 - -

- 微信群二维码 -

- -扫描上方二维码加入我们的微信群,与其他开发者交流讨论! - diff --git a/locales/en/auth.json b/locales/en/auth.json new file mode 100644 index 0000000..06e5495 --- /dev/null +++ b/locales/en/auth.json @@ -0,0 +1,37 @@ +{ + "login": { + "create_account": "Create an account", + "enter_email": "Enter your email below to create your account", + "terms_of_service": "Terms of Service", + "privacy_policy": "Privacy Policy", + "loading": "Loading...", + "login_successful": "Login successful", + "error": "Error", + "email": "Email", + "email_placeholder": "name@example.com", + "password": "Password", + "password_placeholder": "Password", + "sign_in_with_email": "Sign In with Email", + "or_continue_with": "Or continue with" + }, + "social_login": { + "github": "Sign in with Github", + "google": "Sign in with Google", + "apple": "Sign in with Apple", + "microsoft": "Sign in with Microsoft", + "linkedin": "Sign in with LinkedIn", + "twitter": "Sign in with Twitter", + "facebook": "Sign in with Facebook", + "instagram": "Sign in with Instagram", + "snapchat": "Sign in with Snapchat", + "tiktok": "Sign in with TikTok", + "youtube": "Sign in with YouTube" + }, + "company": { + "name": "Acme Inc", + "testimonial": { + "quote": "This library has saved me countless hours of work and helped me deliver stunning designs to my clients faster than ever before.", + "name": "Sofia Davis" + } + } +} diff --git a/locales/en/common.json b/locales/en/common.json new file mode 100644 index 0000000..d0a1661 --- /dev/null +++ b/locales/en/common.json @@ -0,0 +1,24 @@ +{ + "select_language": "Select a language", + "languages": { + "english": "English", + "french": "French", + "german": "German", + "spanish": "Spanish", + "portuguese": "Portuguese", + "russian": "Russian", + "japanese": "Japanese", + "korean": "Korean", + "chinese": "Chinese" + }, + "themes": { + "system": "System", + "light": "Light", + "dark": "Dark" + }, + "fonts": { + "inter": "Inter", + "manrope": "Manrope" + }, + "update": "Update" +} diff --git a/locales/en/errors.json b/locales/en/errors.json new file mode 100644 index 0000000..d467665 --- /dev/null +++ b/locales/en/errors.json @@ -0,0 +1,14 @@ +{ + "name_min_length": "Name must be at least 2 characters.", + "name_max_length": "Name must not be longer than 30 characters.", + "dob_required": "A date of birth is required.", + "language_required": "Please select a language.", + "bio_min_length": "Bio must be at least 10 characters.", + "bio_max_length": "Bio must not be longer than 160 characters.", + "username_min_length": "Username must be at least 2 characters.", + "username_max_length": "Username must not be longer than 30 characters.", + "email_required": "Please select an email to display.", + "notification_type_required": "You need to select a notification type.", + "item_selection_required": "You have to select at least one item.", + "invalid_email_or_password": "Invalid email or password." +} diff --git a/locales/en/forms.json b/locales/en/forms.json new file mode 100644 index 0000000..d88a7b6 --- /dev/null +++ b/locales/en/forms.json @@ -0,0 +1,55 @@ +{ + "actions": { + "update_account": "Update account", + "select_account": "Select account", + "update_preferences": "Update preferences" + }, + "labels": { + "name": "Name", + "email": "Email", + "password": "Password", + "confirm_password": "Confirm password", + "date_of_birth": "Date of birth", + "language": "Language", + "bio": "Bio", + "username": "Username", + "notifications": "Notifications", + "marketing": "Marketing", + "social": "Social", + "security": "Security", + "font": "Font", + "theme": "Theme" + }, + "placeholders": { + "your_name": "Your name", + "your_email": "Your email", + "your_password": "Your password", + "your_date_of_birth": "Your date of birth", + "your_language": "Your language", + "your_bio": "Your bio", + "your_username": "Your username", + "search_language": "Search language" + }, + "descriptions": { + "name": "This is the name that will be displayed on your profile.", + "email": "This is the email that will be displayed on your profile.", + "password": "This is the password that will be used to log in to your account.", + "confirm_password": "This is the password that will be used to confirm your password.", + "date_of_birth": "This is the date of birth that will be displayed on your profile.", + "language": "This is the language that will be displayed on your profile.", + "bio": "This is the bio that will be displayed on your profile.", + "username": "This is the username that will be displayed on your profile.", + "notifications": "This is the notifications that will be used to notify you of new messages, events, etc.", + "marketing": "This is the marketing that will be used to send you marketing emails.", + "social": "This is the social that will be used to send you social emails.", + "security": "This is the security that will be used to send you security emails.", + "font": "This is the font that will be displayed on your profile.", + "theme": "This is the theme that will be displayed on your profile.", + "select_language": "This is the language that will be displayed on your profile.", + "select_font": "This is the font that will be displayed on your profile.", + "select_theme": "This is the theme that will be displayed on your profile." + }, + "errors": { + "no_language_found": "No language found" + } +} diff --git a/locales/en/navigation.json b/locales/en/navigation.json new file mode 100644 index 0000000..3bb5dde --- /dev/null +++ b/locales/en/navigation.json @@ -0,0 +1,39 @@ +{ + "dashboard": "Dashboard", + "forms": "Forms", + "table": "Table", + "tables": "Tables", + "charts": "Charts", + "system": "System", + "settings": "Settings", + "overview": "Overview", + "analysis": "Analysis", + "workplace": "Workplace", + "analytics": "Analytics", + "chat": "Chat", + "email": "Email", + "calendar": "Calendar", + "basic_form": "Basic Form", + "step_form": "Step Form", + "advanced_form": "Advanced Form", + "basic_list": "Basic List", + "table_list": "Table List", + "card_list": "Card List", + "area_chart": "Area Chart", + "bar_chart": "Bar Chart", + "line_chart": "Line Chart", + "pie_chart": "Pie Chart", + "radar_chart": "Radar Chart", + "radial_chart": "Radial Chart", + "tooltip_chart": "Tooltip Chart", + "about": "About", + "user": { + "logout": "Log out", + "profile": "Profile", + "settings": "Settings", + "account": "Account", + "billing": "Billing", + "notifications": "Notifications", + "upgrade_pro": "Upgrade to Pro" + } +} diff --git a/locales/en/settings.json b/locales/en/settings.json new file mode 100644 index 0000000..8493a70 --- /dev/null +++ b/locales/en/settings.json @@ -0,0 +1,119 @@ +{ + "nav": { + "profile": "Profile", + "account": "Account", + "appearance": "Appearance", + "notifications": "Notifications", + "display": "Display", + "theme": "Theme" + }, + "sections": { + "profile": { + "title": "Profile", + "description": "This is the profile that will be displayed on your profile.", + "email_options": "Email options", + "urls": "Urls", + "add_url": "Add url", + "update_profile": "Update profile", + "username": "Username", + "username_description": "This is the username that will be displayed on your profile.", + "email": "Email", + "email_description": "This is the email that will be displayed on your profile.", + "password": "Password", + "password_description": "This is the password that will be used to log in to your account.", + "confirm_password": "Confirm password", + "confirm_password_description": "This is the password that will be used to confirm your password.", + "email_options_description": "This is the email options that will be displayed on your profile.", + "urls_description": "This is the urls that will be displayed on your profile.", + "bio": "Bio", + "bio_description": "This is the bio that will be displayed on your profile." + }, + "appearance": { + "title": "Appearance", + "description": "Customize the appearance of the app. Automatically switch between day and night themes.", + "font": "Font", + "select_font_description": "Select the font for the app.", + "theme": "Theme", + "theme_description": "This is the theme that will be displayed on your profile.", + "system": "System", + "light": "Light", + "dark": "Dark", + "inter": "Inter", + "manrope": "Manrope" + }, + "notifications": { + "title": "Notifications", + "description": "This is the notifications that will be displayed on your profile.", + "email_settings": { + "title": "Email notifications", + "communication": "Communication emails", + "marketing": "Marketing emails", + "social": "Social emails", + "security": "Security emails" + }, + "mobile_settings": { + "use_different": "Use different settings for my mobile devices", + "description": "You can manage your mobile notifications on the mobile settings.", + "page": "Mobile settings page" + }, + "preferences": { + "notify_about": "Notify me about...", + "options": { + "all_messages": "All new messages", + "direct_messages": "Direct messages and mentions", + "nothing": "Nothing" + } + }, + "update_notifications": "Update notifications", + "update_notifications_description": "This is the update notifications that will be displayed on your profile.", + "update_notifications_description_2": "You can manage your mobile notifications on the mobile settings page.", + "update_notifications_description_3": "You can manage your mobile notifications on the mobile settings page." + }, + "display": { + "title": "Display", + "description": "Turn items on or off to control what's displayed in the app.", + "sidebar": { + "title": "Sidebar", + "description": "Turn items on or off to control what's displayed in the app.", + "items": { + "recents": "Recents", + "home": "Home", + "applications": "Applications", + "desktop": "Desktop", + "downloads": "Downloads", + "documents": "Documents" + } + } + }, + "account": { + "title": "Account", + "description": "Update your account settings.", + "name": "Name", + "name_description": "This is your public display name.", + "your_name": "Your name", + "date_of_birth": "Date of birth", + "dob_description": "Your date of birth is used to calculate your age.", + "pick_date": "Pick a date", + "language": "Language", + "language_description": "This is the language that will be used in the application.", + "select_language": "Select a language", + "search_language": "Search language...", + "no_language_found": "No language found", + "update_account": "Update account", + "languages": { + "english": "English", + "french": "French", + "german": "German", + "spanish": "Spanish", + "portuguese": "Portuguese", + "russian": "Russian", + "japanese": "Japanese", + "korean": "Korean", + "chinese": "Chinese" + } + } + }, + "form": { + "you_submitted": "You submitted the following values:" + } +} diff --git a/locales/zh-CN/auth.json b/locales/zh-CN/auth.json new file mode 100644 index 0000000..18bbd99 --- /dev/null +++ b/locales/zh-CN/auth.json @@ -0,0 +1,37 @@ +{ + "login": { + "create_account": "创建账户", + "enter_email": "在下面输入您的电子邮件以创建账户", + "terms_of_service": "服务条款", + "privacy_policy": "隐私政策", + "loading": "加载中...", + "login_successful": "登录成功", + "error": "错误", + "email": "电子邮件", + "email_placeholder": "name@example.com", + "password": "密码", + "password_placeholder": "密码", + "sign_in_with_email": "使用电子邮件登录", + "or_continue_with": "或继续使用" + }, + "social_login": { + "github": "使用 Github 登录", + "google": "使用 Google 登录", + "apple": "使用 Apple 登录", + "microsoft": "使用 Microsoft 登录", + "linkedin": "使用 LinkedIn 登录", + "twitter": "使用 Twitter 登录", + "facebook": "使用 Facebook 登录", + "instagram": "使用 Instagram 登录", + "snapchat": "使用 Snapchat 登录", + "tiktok": "使用 TikTok 登录", + "youtube": "使用 YouTube 登录" + }, + "company": { + "name": "Acme Inc", + "testimonial": { + "quote": "这个库节省了我无数的时间,并帮助我更快地向我的客户交付令人惊叹的设计。", + "name": "Sofia Davis" + } + } +} diff --git a/locales/zh-CN/common.json b/locales/zh-CN/common.json new file mode 100644 index 0000000..129bad7 --- /dev/null +++ b/locales/zh-CN/common.json @@ -0,0 +1,24 @@ +{ + "select_language": "选择语言", + "languages": { + "english": "英语", + "french": "法语", + "german": "德语", + "spanish": "西班牙语", + "portuguese": "葡萄牙语", + "russian": "俄语", + "japanese": "日语", + "korean": "韩语", + "chinese": "中文" + }, + "themes": { + "system": "系统", + "light": "亮", + "dark": "暗" + }, + "fonts": { + "inter": "Inter", + "manrope": "Manrope" + }, + "update": "更新" +} diff --git a/locales/zh-CN/errors.json b/locales/zh-CN/errors.json new file mode 100644 index 0000000..d094082 --- /dev/null +++ b/locales/zh-CN/errors.json @@ -0,0 +1,14 @@ +{ + "name_min_length": "姓名必须至少为2个字符。", + "name_max_length": "姓名不得超过30个字符。", + "dob_required": "出生日期是必需的。", + "language_required": "请选择语言。", + "bio_min_length": "个人简介必须至少为10个字符。", + "bio_max_length": "个人简介不得超过160个字符。", + "username_min_length": "用户名必须至少为2个字符。", + "username_max_length": "用户名不得超过30个字符。", + "email_required": "请选择要显示的电子邮件。", + "notification_type_required": "您需要选择通知类型。", + "item_selection_required": "您必须选择至少一个项目。", + "invalid_email_or_password": "无效的电子邮件或密码。" +} diff --git a/locales/zh-CN/forms.json b/locales/zh-CN/forms.json new file mode 100644 index 0000000..266a3ca --- /dev/null +++ b/locales/zh-CN/forms.json @@ -0,0 +1,55 @@ +{ + "actions": { + "update_account": "更新账户", + "select_account": "选择账户", + "update_preferences": "更新偏好" + }, + "labels": { + "name": "姓名", + "email": "电子邮件", + "password": "密码", + "confirm_password": "确认密码", + "date_of_birth": "出生日期", + "language": "语言", + "bio": "个人简介", + "username": "用户名", + "notifications": "通知", + "marketing": "营销", + "social": "社交", + "security": "安全", + "font": "字体", + "theme": "主题" + }, + "placeholders": { + "your_name": "您的姓名", + "your_email": "您的电子邮件", + "your_password": "您的密码", + "your_date_of_birth": "您的出生日期", + "your_language": "您的语言", + "your_bio": "您的个人简介", + "your_username": "您的用户名", + "search_language": "搜索语言" + }, + "descriptions": { + "name": "这是将在您的个人资料中显示的姓名。", + "email": "这是将在您的个人资料中显示的电子邮件。", + "password": "这是将用于登录您的账户的密码。", + "confirm_password": "这是将用于确认您的密码的密码。", + "date_of_birth": "这是将在您的个人资料中显示的出生日期。", + "language": "这是将在您的个人资料中显示的语言。", + "bio": "这是将在您的个人资料中显示的个人简介。", + "username": "这是将在您的个人资料中显示的用户名。", + "notifications": "这是将用于通知您的新消息、事件等的通知。", + "marketing": "这是将用于发送营销电子邮件的营销。", + "social": "这是将用于发送社交电子邮件的社交。", + "security": "这是将用于发送安全电子邮件的安全。", + "font": "这是将在您的个人资料中显示的字体。", + "theme": "这是将在您的个人资料中显示的主题。", + "select_language": "这是将在您的个人资料中显示的语言。", + "select_font": "这是将在您的个人资料中显示的字体。", + "select_theme": "这是将在您的个人资料中显示的主题。" + }, + "errors": { + "no_language_found": "没有找到语言" + } +} diff --git a/locales/zh-CN/navigation.json b/locales/zh-CN/navigation.json new file mode 100644 index 0000000..2e39d9f --- /dev/null +++ b/locales/zh-CN/navigation.json @@ -0,0 +1,39 @@ +{ + "dashboard": "仪表板", + "forms": "表单", + "table": "表格", + "tables": "表格", + "charts": "图表", + "system": "系统", + "settings": "设置", + "overview": "概览", + "analysis": "分析", + "workplace": "工作台", + "analytics": "分析", + "chat": "聊天", + "email": "电子邮件", + "calendar": "日历", + "basic_form": "基本表单", + "step_form": "步骤表单", + "advanced_form": "高级表单", + "basic_list": "基本列表", + "table_list": "表格列表", + "card_list": "卡片列表", + "area_chart": "面积图", + "bar_chart": "柱状图", + "line_chart": "折线图", + "pie_chart": "饼图", + "radar_chart": "雷达图", + "radial_chart": "径向图", + "tooltip_chart": "提示图", + "about": "关于", + "user": { + "logout": "登出", + "profile": "个人资料", + "settings": "设置", + "account": "账户", + "billing": "账单", + "notifications": "通知", + "upgrade_pro": "升级到专业版" + } +} diff --git a/locales/zh-CN/settings.json b/locales/zh-CN/settings.json new file mode 100644 index 0000000..baab326 --- /dev/null +++ b/locales/zh-CN/settings.json @@ -0,0 +1,124 @@ +{ + "nav": { + "profile": "个人资料", + "account": "账户", + "appearance": "外观", + "notifications": "通知", + "display": "显示", + "theme": "主题" + }, + "sections": { + "profile": { + "title": "个人资料", + "description": "这是您在网站上看到的个人资料。", + "email_options": "电子邮件选项", + "urls": "链接", + "add_url": "添加链接", + "update_profile": "更新个人资料", + "username": "用户名", + "username_description": "这是将在您的个人资料中显示的用户名。", + "email": "电子邮件", + "email_description": "这是将在您的个人资料中显示的电子邮件。", + "password": "密码", + "password_description": "这是将用于登录您的账户的密码。", + "confirm_password": "确认密码", + "confirm_password_description": "这是将用于确认您的密码的密码。", + "email_options_description": "这是将在您的个人资料中显示的电子邮件选项。", + "urls_description": "这是将在您的个人资料中显示的链接。", + "bio": "个人简介", + "bio_description": "这是将在您的个人资料中显示的个人简介。" + }, + "appearance": { + "title": "外观", + "description": "自定义应用程序的外观。自动在白天和黑夜之间切换主题。", + "font": "字体", + "select_font_description": "选择应用程序的字体。", + "theme": "主题", + "theme_description": "这是将在您的个人资料中显示的主题。", + "system": "系统", + "light": "亮色", + "dark": "暗色", + "inter": "Inter", + "manrope": "Manrope" + }, + "notifications": { + "title": "通知", + "description": "这是您在网站上看到的通知。", + "email_settings": { + "title": "电子邮件通知", + "communication": "通信电子邮件", + "marketing": "营销电子邮件", + "social": "社交电子邮件", + "security": "安全电子邮件" + }, + "mobile_settings": { + "use_different": "为我的移动设备使用不同的设置", + "description": "您可以在移动设置页面中管理您的移动通知。", + "page": "移动设置" + }, + "preferences": { + "notify_about": "通知我关于", + "options": { + "all_messages": "所有新消息", + "mentions": "所有新提及", + "reactions": "所有新反应", + "followers": "所有新关注者", + "direct_messages": "直接消息和提及", + "comments": "所有新评论", + "replies": "所有新回复", + "posts": "所有新帖子", + "nothing": "无" + } + }, + "update_notifications": "更新通知", + "update_notifications_description": "这是将在您的个人资料中显示的更新通知。", + "update_notifications_description_2": "您可以在移动设置页面中管理您的移动通知。", + "update_notifications_description_3": "您可以在移动设置页面中管理您的移动通知。" + }, + "display": { + "title": "显示", + "description": "打开或关闭项目以控制应用程序中显示的内容。", + "sidebar": { + "title": "侧边栏", + "description": "打开或关闭项目以控制应用程序中显示的内容。", + "items": { + "recents": "最近", + "home": "主页", + "applications": "应用程序", + "desktop": "桌面", + "downloads": "下载", + "documents": "文档" + } + } + }, + "account": { + "title": "账户", + "name": "姓名", + "name_description": "这是您的公开显示名称。", + "your_name": "您的姓名", + "date_of_birth": "出生日期", + "dob_description": "您的出生日期用于计算您的年龄。", + "pick_date": "选择日期", + "language": "语言", + "language_description": "这是将在应用程序中使用的语言。", + "select_language": "选择语言", + "search_language": "搜索语言...", + "no_language_found": "未找到语言", + "update_account": "更新账户", + "languages": { + "english": "英语", + "french": "法语", + "german": "德语", + "spanish": "西班牙语", + "portuguese": "葡萄牙语", + "russian": "俄语", + "japanese": "日语", + "korean": "韩语", + "chinese": "中文" + } + } + }, + "form": { + "you_submitted": "您提交了以下值:" + } +} diff --git a/src/components/app-sidebar.tsx b/src/components/app-sidebar.tsx new file mode 100644 index 0000000..3d47afc --- /dev/null +++ b/src/components/app-sidebar.tsx @@ -0,0 +1,67 @@ +import { env } from "@env" +import { + Command, + LifeBuoy, + Send, +} from "lucide-react" +import * as React from "react" +import { Link } from "react-router-dom" + +import { NavMain } from "@/components/nav-main" +import { NavSecondary } from "@/components/nav-secondary" +import { NavUser } from "@/components/nav-user" +import { + Sidebar, + SidebarContent, + SidebarFooter, + SidebarHeader, + SidebarMenu, + SidebarMenuButton, + SidebarMenuItem, +} from "@/components/ui/sidebar" + +import { menus } from "./layout/sidebar/data" + +const navSecondary = [ + { + title: "Support", + url: "#", + icon: LifeBuoy, + }, + { + title: "Feedback", + url: "#", + icon: Send, + }, +] + +export function AppSidebar({ ...props }: React.ComponentProps) { + return ( + + + + + + +
+ +
+
+ {env.VITE_APP_NAME} + Enterprise +
+ +
+
+
+
+ + + + + + + +
+ ) +} diff --git a/src/components/fallback.tsx b/src/components/fallback.tsx index ba4beaf..d4e0b57 100644 --- a/src/components/fallback.tsx +++ b/src/components/fallback.tsx @@ -3,8 +3,10 @@ import { Icons } from "./icons" export function Fallback() { return (
- - Loading... +
+ + Loading... +
) } diff --git a/src/components/language-switch.tsx b/src/components/language-switch.tsx index b55e64d..67d77c4 100644 --- a/src/components/language-switch.tsx +++ b/src/components/language-switch.tsx @@ -10,7 +10,7 @@ import { import { languages } from "@/i18n" export const LanguageSwitch = () => { - const { i18n } = useTranslation() + const { i18n, t } = useTranslation("common") const changeLanguage = (lng: string) => { i18n.changeLanguage(lng) @@ -18,8 +18,8 @@ export const LanguageSwitch = () => { return ( - span]:line-clamp-1 [&>span]:flex [&>span]:w-full [&>span]:items-center [&>span]:gap-1 [&>span]:truncate [&_svg]:size-4 [&_svg]:shrink-0", - isCollapsed && - "flex size-9 shrink-0 items-center justify-center p-0 [&>span]:w-auto [&>svg]:hidden", - )} - aria-label="Select account" - > - - {accounts.find((account) => account.email === selectedAccount)?.icon} - - {accounts.find((account) => account.email === selectedAccount) - ?.label} - - - - - {accounts.map((account) => ( - -
- {account.icon} - {account.email} -
-
- ))} -
- - ) -} diff --git a/src/components/layout/sidebar/data.tsx b/src/components/layout/sidebar/data.tsx index 46ed69e..555bc2a 100644 --- a/src/components/layout/sidebar/data.tsx +++ b/src/components/layout/sidebar/data.tsx @@ -16,6 +16,7 @@ import { Radar, Radical, Settings, + Table, TableProperties, Trash2, } from "lucide-react" @@ -65,6 +66,7 @@ export const menus: IMenu[] = [ { title: "dashboard", icon: Gauge, + to: "/dashboard", children: [ { title: "overview", @@ -84,12 +86,13 @@ export const menus: IMenu[] = [ to: "/dashboard/workplace", }, ], - to: "/dashboard", + }, { title: "forms", label: "12", icon: MessagesSquare, + to: "/form", children: [ { title: "basic_form", @@ -108,11 +111,11 @@ export const menus: IMenu[] = [ to: "/form/advanced-form", }, ], - to: "/form", }, { title: "table", - icon: MessagesSquare, + to: "/list", + icon: Table, children: [ { title: "basic_list", @@ -133,11 +136,11 @@ export const menus: IMenu[] = [ to: "/list/card-list", }, ], - to: "/list", }, { title: "charts", - icon: MessagesSquare, + icon: ChartLine, + to: "/charts", children: [ { title: "area_chart", @@ -175,7 +178,6 @@ export const menus: IMenu[] = [ to: "/charts/tooltip", }, ], - to: "/charts", }, { title: "settings", @@ -185,6 +187,7 @@ export const menus: IMenu[] = [ { title: "system", icon: Info, + to: "/system", children: [ { title: "about", @@ -192,6 +195,5 @@ export const menus: IMenu[] = [ to: "/system/about", }, ], - to: "/system", }, ] as const diff --git a/src/components/layout/sidebar/index.tsx b/src/components/layout/sidebar/index.tsx deleted file mode 100644 index c658af8..0000000 --- a/src/components/layout/sidebar/index.tsx +++ /dev/null @@ -1,150 +0,0 @@ -import * as React from "react" -import { useTranslation } from "react-i18next" -import { NavLink } from "react-router-dom" - -import { buttonVariants } from "@/components/ui/button" -import { cn } from "@/lib/utils" -import { ScrollArea } from "@/ui/scroll-area" -import { Tooltip, TooltipContent, TooltipTrigger } from "@/ui/tooltip" - -import { AccountSwitcher } from "./account-switcher" -import { accounts, menus } from "./data" - -interface SidebarProps extends React.HTMLAttributes { - isCollapsed: boolean -} - -export function Sidebar({ className, isCollapsed }: SidebarProps) { - const { t } = useTranslation() - return ( -
-
- {/* -

- -

- */} - -
- - {!isCollapsed ? (menus.map((menuItem) => ( -
- {menuItem.children && ( - <> -

- {t(`menus.${menuItem.title}` as never)} -

-
- {menuItem.children.map((child) => ( - - cn( - buttonVariants({ - variant: isActive ? "default" : "ghost", - }), - "w-full justify-start capitalize", - - )} - > - - - {t(`menus.${child.title}` as never)} - - {child.label && ( - - {child.label} - - )} - - ))} -
- - )} - {!menuItem.children && menuItem.to && ( - - cn( - buttonVariants({ - variant: isActive ? "default" : "ghost", - }), - "w-full justify-start capitalize", - - )} - > - - {t(`menus.${menuItem.title}` as never)} - {menuItem.label && ( - - {menuItem.label} - - )} - - )} -
- ))) : ( -
-
- {menus.map((menuItem) => ( - - {menuItem.children && ( - - {menuItem.children.map((child) => ( - - - cn( - buttonVariants({ - variant: isActive ? "default" : "ghost", - size: "icon", - }), - "size-9", - - )} - > - - - {t(`menus.${child.title}` as never)} - - - - - {t(`menus.${child.title}` as never)} - {child.label && ( - - {child.label} - - )} - - - ))} - - )} - - ))} -
-
- )} -
-
- ) -} diff --git a/src/components/layout/sidebar/mobile-nav.tsx b/src/components/layout/sidebar/mobile-nav.tsx deleted file mode 100644 index ca1a21d..0000000 --- a/src/components/layout/sidebar/mobile-nav.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import { Menu } from "lucide-react" - -import { Button } from "@/components/ui/button" -import { Sheet, SheetContent, SheetDescription, SheetTitle, SheetTrigger } from "@/components/ui/sheet" - -import { Sidebar } from "." - -export function MobileNav() { - return ( - - - - - - Mobile Nav - Mobile Nav - - - - ) -} diff --git a/src/components/nav-breadcrumb.tsx b/src/components/nav-breadcrumb.tsx new file mode 100644 index 0000000..f61b955 --- /dev/null +++ b/src/components/nav-breadcrumb.tsx @@ -0,0 +1,97 @@ +import * as React from "react" +import { useTranslation } from "react-i18next" +import { Link, useLocation } from "react-router-dom" + +import { menus } from "@/components/layout/sidebar/data" +import { + Breadcrumb, + BreadcrumbItem, + BreadcrumbLink, + BreadcrumbList, + BreadcrumbPage, + BreadcrumbSeparator, +} from "@/components/ui/breadcrumb" +import type { IMenu } from "@/models/menu" + +interface Breadcrumb { + title: string + to: string + isLast: boolean +} + +export function NavBreadcrumb() { + const location = useLocation() + const { t } = useTranslation("navigation") + + const findMenuPath = ( + pathname: string, + items: IMenu[], + parents: IMenu[] = [], + ): (IMenu & { parents: IMenu[] }) | null => { + for (const item of items) { + if (item.to === pathname) { + return { ...item, parents } + } + + if (item.children?.length) { + const found = findMenuPath(pathname, item.children, [...parents, item]) + if (found) return found + } + } + return null + } + + const buildBreadcrumbs = (): Breadcrumb[] => { + const menuPath = findMenuPath(location.pathname, menus) + if (!menuPath) return [] + + const breadcrumbs: Breadcrumb[] = [] + + // 添加父级菜单 + menuPath.parents.forEach((parent) => { + breadcrumbs.push({ + title: parent.title, + to: parent.to, + isLast: false, + }) + }) + + // 添加当前菜单 + breadcrumbs.push({ + title: menuPath.title, + to: menuPath.to, + isLast: true, + }) + + return breadcrumbs + } + + const breadcrumbs = buildBreadcrumbs() + + if (breadcrumbs.length === 0) { + return null + } + + return ( + + + {breadcrumbs.map((item) => ( + + + {!item.isLast ? ( + + + {t(item.title)} + + + ) : ( + {t(item.title)} + )} + + {!item.isLast && } + + ))} + + + ) +} diff --git a/src/components/nav-main.tsx b/src/components/nav-main.tsx new file mode 100644 index 0000000..d631bf0 --- /dev/null +++ b/src/components/nav-main.tsx @@ -0,0 +1,92 @@ +import { ChevronRight } from "lucide-react" +import { useTranslation } from "react-i18next" +import { Link, useLocation } from "react-router-dom" + +import { + Collapsible, + CollapsibleContent, + CollapsibleTrigger, +} from "@/components/ui/collapsible" +import { + SidebarGroup, + SidebarGroupLabel, + SidebarMenu, + SidebarMenuAction, + SidebarMenuButton, + SidebarMenuItem, + SidebarMenuSub, + SidebarMenuSubButton, + SidebarMenuSubItem, +} from "@/components/ui/sidebar" +import type { IMenu } from "@/models/menu" + +export function NavMain({ + items, +}: { + items: IMenu[] +}) { + const { t } = useTranslation("navigation") + const location = useLocation() + + const isPathActive = (path: string) => { + return location.pathname === path || location.pathname.startsWith(`${path}/`) + } + + const isParentActive = (item: IMenu) => { + return item.children?.some((child) => isPathActive(child.to)) + } + + return ( + + Platform + + {items.map((item) => ( + + + + + + {t(item.title)} + + + {item.children?.length ? ( + <> + + + + Toggle + + + + + {item.children?.map((subItem) => ( + + + + {t(subItem.title)} + + + + ))} + + + + ) : null} + + + ))} + + + ) +} diff --git a/src/components/nav-projects.tsx b/src/components/nav-projects.tsx new file mode 100644 index 0000000..d313952 --- /dev/null +++ b/src/components/nav-projects.tsx @@ -0,0 +1,87 @@ +import type { LucideIcon } from "lucide-react" +import { + Folder, + MoreHorizontal, + Share, + Trash2, +} from "lucide-react" + +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuSeparator, + DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu" +import { + SidebarGroup, + SidebarGroupLabel, + SidebarMenu, + SidebarMenuAction, + SidebarMenuButton, + SidebarMenuItem, + useSidebar, +} from "@/components/ui/sidebar" + +export function NavProjects({ + projects, +}: { + projects: { + name: string + url: string + icon: LucideIcon + }[] +}) { + const { isMobile } = useSidebar() + + return ( + + Projects + + {projects.map((item) => ( + + + + + {item.name} + + + + + + + More + + + + + + View Project + + + + Share Project + + + + + Delete Project + + + + + ))} + + + + More + + + + + ) +} diff --git a/src/components/nav-secondary.tsx b/src/components/nav-secondary.tsx new file mode 100644 index 0000000..4fa1d87 --- /dev/null +++ b/src/components/nav-secondary.tsx @@ -0,0 +1,40 @@ +import type { LucideIcon } from "lucide-react" +import * as React from "react" + +import { + SidebarGroup, + SidebarGroupContent, + SidebarMenu, + SidebarMenuButton, + SidebarMenuItem, +} from "@/components/ui/sidebar" + +export function NavSecondary({ + items, + ...props +}: { + items: { + title: string + url: string + icon: LucideIcon + }[] +} & React.ComponentPropsWithoutRef) { + return ( + + + + {items.map((item) => ( + + + + + {item.title} + + + + ))} + + + + ) +} diff --git a/src/components/nav-user.tsx b/src/components/nav-user.tsx new file mode 100644 index 0000000..5afdbb5 --- /dev/null +++ b/src/components/nav-user.tsx @@ -0,0 +1,109 @@ +import { + BadgeCheck, + Bell, + ChevronsUpDown, + CreditCard, + LogOut, + Sparkles, +} from "lucide-react" +import { useTranslation } from "react-i18next" + +import { + Avatar, + AvatarFallback, + AvatarImage, +} from "@/components/ui/avatar" +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuGroup, + DropdownMenuItem, + DropdownMenuLabel, + DropdownMenuSeparator, + DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu" +import { + SidebarMenu, + SidebarMenuButton, + SidebarMenuItem, + useSidebar, +} from "@/components/ui/sidebar" +import { useUser } from "@/hooks/query/use-user" + +export function NavUser() { + const { isMobile } = useSidebar() + const { t } = useTranslation("navigation") + const { data: user } = useUser() + return ( + + + + + + + + CN + +
+ {user.username} + {user.email} +
+ +
+
+ + +
+ + + + {user.username.slice(0, 2)} + + +
+ {user.username} + {user.email} +
+
+
+ + + + + {t("user.upgrade_pro")} + + + + + + + {t("user.account")} + + + + {t("user.billing")} + + + + {t("user.notifications")} + + + + + + {t("user.logout")} + +
+
+
+
+ ) +} diff --git a/src/components/ui/breadcrumb.tsx b/src/components/ui/breadcrumb.tsx index 311e71d..cfdc9ac 100644 --- a/src/components/ui/breadcrumb.tsx +++ b/src/components/ui/breadcrumb.tsx @@ -1,6 +1,6 @@ +import * as React from "react" import { ChevronRightIcon, DotsHorizontalIcon } from "@radix-ui/react-icons" import { Slot } from "@radix-ui/react-slot" -import * as React from "react" import { cn } from "@/lib/utils" @@ -20,7 +20,7 @@ const BreadcrumbList = React.forwardRef< ref={ref} className={cn( "flex flex-wrap items-center gap-1.5 break-words text-sm text-muted-foreground sm:gap-2.5", - className, + className )} {...props} /> @@ -80,7 +80,7 @@ const BreadcrumbSeparator = ({