diff --git a/dashboard/src/components/NavigationItems.vue b/dashboard/src/components/NavigationItems.vue index 2926bfe..d47e0df 100644 --- a/dashboard/src/components/NavigationItems.vue +++ b/dashboard/src/components/NavigationItems.vue @@ -67,7 +67,7 @@ export default { disabled: enforce2FA, }, { - name: '站点', + name: this.$t('Sites'), icon: () => h(PanelTopInactive), route: '/sites', isActive: diff --git a/dashboard/src/objects/common/index.ts b/dashboard/src/objects/common/index.ts index efbffb4..4d751c9 100644 --- a/dashboard/src/objects/common/index.ts +++ b/dashboard/src/objects/common/index.ts @@ -9,18 +9,19 @@ import type { } from './types'; import { trialDays } from '../../utils/site'; import { planTitle } from '../../utils/format'; +import { t } from '../../utils/i18n'; export const unreachable = Error('unreachable'); // used to indicate that a codepath is unreachable export const clusterOptions = [ '', - '中国大陆', - '中国香港', - '美国-洛杉矶', - '新加坡', - '英国-伦敦', - '德国-法兰克福', - '阿联酋-迪拜', + t('China'), + t('Hong Kong'), + t('US - Los Angeles'), + t('Singapore'), + t('UK - London'), + t('Germany - Frankfurt'), + t('UAE - Dubai'), ]; export function getUpsellBanner(site: DocumentResource, title: string) { @@ -38,7 +39,7 @@ export function getUpsellBanner(site: DocumentResource, title: string) { id: site.name, type: 'gray', button: { - label: '升级计划', + label: t('Upgrade Plan'), variant: 'outline', onClick() { let SitePlansDialog = defineAsyncComponent( @@ -53,7 +54,7 @@ export function getUpsellBanner(site: DocumentResource, title: string) { export function getSitesTabColumns(forBenchTab: boolean) { return [ { - label: '站点', + label: t('Sites'), fieldname: 'host_name', format(value, row) { return value || row.name; @@ -64,13 +65,13 @@ export function getSitesTabColumns(forBenchTab: boolean) { }, }, { - label: '状态', + label: t('Status'), fieldname: 'status', type: 'Badge', width: 0.5, }, { - label: '区域', + label: t('Region'), fieldname: 'cluster_title', width: 0.5, prefix(row) { @@ -83,7 +84,7 @@ export function getSitesTabColumns(forBenchTab: boolean) { }, }, { - label: '计划', + label: t('Plan Type'), width: 0.5, format(value, row) { if (row.trial_end_date) { @@ -99,29 +100,29 @@ export function siteTabFilterControls() { return [ { type: 'select', - label: '状态', + label: t('Status'), fieldname: 'status', options: [ { label: '', value: '' }, - { label: '激活', value: 'Active' }, - { label: '未激活', value: 'Inactive' }, - { label: '已暂停', value: 'Suspended' }, - { label: '已损坏', value: 'Broken' } + { label: t('Active'), value: 'Active' }, + { label: t('Inactive'), value: 'Inactive' }, + { label: t('Suspended'), value: 'Suspended' }, + { label: t('Broken'), value: 'Broken' } ], }, { type: 'select', - label: '区域', + label: t('Region'), fieldname: 'cluster', options: [ '', - '中国大陆', - '中国香港', - '美国-洛杉矶', - '新加坡', - '英国-伦敦', - '德国-法兰克福', - '阿联酋-迪拜', + t('China'), + t('Hong Kong'), + t('US - Los Angeles'), + t('Singapore'), + t('UK - London'), + t('Germany - Frankfurt'), + t('UAE - Dubai'), ], }, ]; @@ -129,7 +130,7 @@ export function siteTabFilterControls() { export function sitesTabRoute(r: Row) { return { - name: '站点详情', + name: 'Site Detail', params: { name: r.name }, } satisfies Route as Route; } \ No newline at end of file diff --git a/dashboard/src/objects/server.js b/dashboard/src/objects/server.js index 2d08383..d80a895 100644 --- a/dashboard/src/objects/server.js +++ b/dashboard/src/objects/server.js @@ -8,6 +8,7 @@ import { duration, planTitle, userCurrency } from '../utils/format'; import { trialDays } from '../utils/site'; import { getJobsTab } from './common/jobs'; import { tagTab } from './common/tags'; +import { t } from '../utils/i18n'; export default { pagetype: 'Server', @@ -23,7 +24,7 @@ export default { }, list: { route: '/servers', - title: '服务器', + title: t('Servers'), fields: [ 'title', 'database_server', @@ -37,27 +38,27 @@ export default { return [ { type: 'select', - label: '状态', + label: t('Status'), fieldname: 'status', options: [ { label: '', value: '' }, - { label: '激活', value: 'Active' }, - { label: '待定', value: 'Pending' } + { label: t('Active'), value: 'Active' }, + { label: t('Pending'), value: 'Pending' } ] }, { type: 'select', - label: '区域', + label: t('Region'), fieldname: 'cluster', options: [ '', - '中国大陆', - '中国香港', - '美国-洛杉矶', - '新加坡', - '英国-伦敦', - '德国-法兰克福', - '阿联酋-迪拜', + t('China'), + t('Hong Kong'), + t('US - Los Angeles'), + t('Singapore'), + t('UK - London'), + t('Germany - Frankfurt'), + t('UAE - Dubai'), ] } ]; @@ -65,7 +66,7 @@ export default { orderBy: 'creation desc', columns: [ { - label: '服务器', + label: t('Servers'), fieldname: 'name', width: 1.5, class: 'font-medium', @@ -73,15 +74,15 @@ export default { return row.title || value; } }, - { label: '状态', fieldname: 'status', type: 'Badge', width: 0.8 }, + { label: t('Status'), fieldname: 'status', type: 'Badge', width: 0.8 }, { - label: '应用服务器计划', + label: t('Application Server Plan'), format(value, row) { return planTitle(row); } }, { - label: '数据库服务器计划', + label: t('Database Server Plan'), fieldname: 'db_plan', format(value) { if (!value) return ''; @@ -89,7 +90,7 @@ export default { } }, { - label: '区域', + label: t('Region'), fieldname: 'cluster', format(value, row) { return row.cluster_title || value; @@ -105,7 +106,7 @@ export default { ], primaryAction({ listResource: servers }) { return { - label: '新建服务器', + label: t('New Server'), variant: 'solid', slots: { prefix: icon('plus') @@ -127,7 +128,7 @@ export default { breadcrumbs({ documentResource: server }) { return [ { - label: '服务器', + label: t('Servers'), route: '/servers' }, { @@ -141,8 +142,8 @@ export default { return [ { - label: '模拟服务器所有者', - title: '模拟服务器所有者', // for label to pop-up on hover + label: t('Impersonate Server Owner'), + title: t('Impersonate Server Owner'), // for label to pop-up on hover slots: { icon: defineAsyncComponent(() => import('~icons/lucide/venetian-mask') @@ -155,16 +156,16 @@ export default { } }, { - label: '选项', + label: t('Options'), button: { - label: '选项', + label: t('Options'), slots: { icon: icon('more-horizontal') } }, options: [ { - label: '在 Desk 中查看', + label: t('View in Desk'), icon: icon('external-link'), condition: () => $team.pg?.is_desk_user, onClick() { @@ -179,7 +180,7 @@ export default { } }, { - label: '访问服务器', + label: t('Access Server'), icon: icon('external-link'), condition: () => server.pg.status === 'Active' && $team.pg?.is_desk_user, @@ -193,7 +194,7 @@ export default { }, tabs: [ { - label: '概览', + label: t('Overview'), icon: icon('home'), route: 'overview', type: 'Component', @@ -205,7 +206,7 @@ export default { } }, { - label: '分析', + label: t('Analytics'), icon: icon('bar-chart-2'), route: 'analytics', type: 'Component', @@ -219,7 +220,7 @@ export default { } }, { - label: '站点', + label: t('Sites'), icon: icon(LucideAppWindow), route: 'sites', type: 'list', @@ -247,19 +248,19 @@ export default { return [ { type: 'select', - label: '状态', + label: t('Status'), fieldname: 'status', options: [ { label: '', value: '' }, - { label: '激活', value: 'Active' }, - { label: '未激活', value: 'Inactive' }, - { label: '已暂停', value: 'Suspended' }, - { label: '损坏', value: 'Broken' } + { label: t('Active'), value: 'Active' }, + { label: t('Inactive'), value: 'Inactive' }, + { label: t('Suspended'), value: 'Suspended' }, + { label: t('Broken'), value: 'Broken' } ] }, { type: 'link', - label: '版本', + label: t('Version'), fieldname: 'group.version', options: { pagetype: 'Jingrow Version' @@ -267,7 +268,7 @@ export default { }, { type: 'link', - label: '站点分组', + label: t('Release Group'), fieldname: 'group', options: { pagetype: 'Release Group' @@ -275,7 +276,7 @@ export default { }, { type: 'link', - label: '标签', + label: t('Tag'), fieldname: 'tags.tag', options: { pagetype: 'Jcloud Tag', @@ -288,7 +289,7 @@ export default { }, columns: [ { - label: '站点', + label: t('Sites'), fieldname: 'host_name', width: 1.5, class: 'font-medium', @@ -296,9 +297,9 @@ export default { return value || row.name; } }, - { label: '状态', fieldname: 'status', type: 'Badge', width: 0.6 }, + { label: t('Status'), fieldname: 'status', type: 'Badge', width: 0.6 }, { - label: '计划', + label: t('Plan Type'), fieldname: 'plan', width: 0.85, format(value, row) { @@ -312,18 +313,18 @@ export default { china ? row.price_cny : row.price_usd, 0 ); - return `${formattedValue}/月`; + return `${formattedValue}/${t('Monthly')}`; } return row.plan_title; } }, { - label: '站点分组', + label: t('Release Group'), fieldname: 'group_title', width: '15rem' }, { - label: '版本', + label: t('Version'), fieldname: 'version', width: 0.5 } @@ -331,7 +332,7 @@ export default { } }, { - label: '站点分组', + label: t('Release Group'), icon: icon('package'), route: 'groups', type: 'list', @@ -342,24 +343,24 @@ export default { }, fields: [{ apps: ['app'] }, { servers: ['server'] }], columns: [ - { label: '标题', fieldname: 'title' }, + { label: t('Title'), fieldname: 'title' }, { - label: '状态', + label: t('Status'), fieldname: 'active_benches', type: 'Badge', width: 0.5, format: (value, row) => { - if (!value) return '等待部署'; - else return '激活'; + if (!value) return t('Waiting for Deployment'); + else return t('Active'); } }, { - label: '版本', + label: t('Version'), fieldname: 'version', width: 0.5 }, { - label: '应用', + label: t('App'), fieldname: 'app', format: (value, row) => { return (row.apps || []).map(d => d.app).join(', '); @@ -367,7 +368,7 @@ export default { width: '25rem' }, { - label: '站点', + label: t('Sites'), fieldname: 'site_count', width: 0.25 } @@ -376,7 +377,7 @@ export default { return [ { type: 'link', - label: '版本', + label: t('Version'), fieldname: 'version', options: { pagetype: 'Jingrow Version' @@ -384,7 +385,7 @@ export default { }, { type: 'link', - label: '标签', + label: t('Tag'), fieldname: 'tags.tag', options: { pagetype: 'Jcloud Tag', @@ -403,7 +404,7 @@ export default { }, primaryAction({ listResource: benches, documentResource: server }) { return { - label: '新建站点分组', + label: t('New Release Group'), slots: { prefix: icon('plus') }, @@ -419,7 +420,7 @@ export default { }, getJobsTab('Server'), { - label: '执行', + label: t('Execute'), icon: icon('play'), childrenRoutes: ['Server Play'], route: 'plays', @@ -430,7 +431,7 @@ export default { return [ { type: 'select', - label: '服务器', + label: t('Servers'), fieldname: 'server', options: [ server.pg.name, @@ -462,23 +463,23 @@ export default { fields: ['server', 'end'], columns: [ { - label: '执行', + label: t('Execute'), fieldname: 'play', width: 2 }, { - label: '状态', + label: t('Status'), fieldname: 'status', type: 'Badge', width: 0.5 }, { - label: '服务器', + label: t('Servers'), fieldname: 'server', width: 2 }, { - label: '持续时间', + label: t('Duration'), fieldname: 'duration', width: 0.5, format(value, row) { @@ -496,7 +497,7 @@ export default { } }, { - label: '操作', + label: t('Actions'), icon: icon('sliders'), route: 'actions', type: 'Component', diff --git a/dashboard/src/objects/site.js b/dashboard/src/objects/site.js index 632afad..92b1411 100644 --- a/dashboard/src/objects/site.js +++ b/dashboard/src/objects/site.js @@ -23,6 +23,7 @@ import { clusterOptions, getUpsellBanner } from './common'; import { getAppsTab } from './common/apps'; import { isMobile } from '../utils/device'; import { getJobsTab } from './common/jobs'; +import { t } from '../utils/i18n'; export default { pagetype: 'Site', @@ -64,7 +65,7 @@ export default { }, list: { route: '/sites', - title: '站点', + title: t('Sites'), fields: [ 'plan.plan_title as plan_title', 'plan.price_usd as price_usd', @@ -84,20 +85,20 @@ export default { return [ { type: 'select', - label: '状态', + label: t('Status'), fieldname: 'status', options: [ { label: '', value: '' }, - { label: '激活', value: 'Active' }, - { label: '未激活', value: 'Inactive' }, - { label: '已暂停', value: 'Suspended' }, - { label: '损坏', value: 'Broken' }, - { label: '已归档', value: 'Archived' }, + { label: t('Active'), value: 'Active' }, + { label: t('Inactive'), value: 'Inactive' }, + { label: t('Suspended'), value: 'Suspended' }, + { label: t('Broken'), value: 'Broken' }, + { label: t('Archived'), value: 'Archived' }, ], }, { type: 'link', - label: '版本', + label: t('Version'), fieldname: 'group.version', options: { pagetype: 'Jingrow Version', @@ -105,7 +106,7 @@ export default { }, { type: 'link', - label: '站点分组', + label: t('Release Group'), fieldname: 'group', options: { pagetype: 'Release Group', @@ -113,13 +114,13 @@ export default { }, { type: 'select', - label: '区域', + label: t('Region'), fieldname: 'cluster', options: clusterOptions, }, { type: 'link', - label: '标签', + label: t('Tag'), fieldname: 'tags.tag', options: { pagetype: 'Jcloud Tag', @@ -132,7 +133,7 @@ export default { }, columns: [ { - label: '站点', + label: t('Sites'), fieldname: 'host_name', width: 1.5, class: 'font-medium', @@ -141,29 +142,29 @@ export default { }, }, { - label: '状态', + label: t('Status'), fieldname: 'status', type: 'Badge', width: '140px', format(value) { const statusMap = { - 'Active': '激活', - 'Inactive': '未激活', - 'Suspended': '已暂停', - 'Broken': '损坏', - 'Archived': '已归档', - 'Pending': '待处理', - 'Installing': '安装中', - 'Update Available': '可更新', - 'Running': '运行中', - 'Success': '成功', - 'Failure': '失败' + 'Active': t('Active'), + 'Inactive': t('Inactive'), + 'Suspended': t('Suspended'), + 'Broken': t('Broken'), + 'Archived': t('Archived'), + 'Pending': t('Pending'), + 'Installing': t('Installing'), + 'Update Available': t('Update Available'), + 'Running': t('Running'), + 'Success': t('Success'), + 'Failure': t('Failure') }; return statusMap[value] || value; } }, { - label: '计划', + label: t('Plan Type'), fieldname: 'plan', width: 0.85, format(value, row) { @@ -177,13 +178,13 @@ export default { china ? row.price_cny : row.price_usd, 0, ); - return `${formattedValue}/月`; + return `${formattedValue}/${t('Monthly')}`; } return row.plan_title; }, }, { - label: '区域', + label: t('Region'), fieldname: 'cluster', width: 1, format(value, row) { @@ -198,20 +199,20 @@ export default { }, }, { - label: '站点分组', + label: t('Release Group'), fieldname: 'group', width: '15rem', format(value, row) { - return row.group_public ? '公域' : row.group_title || value; + return row.group_public ? t('Public') : row.group_title || value; }, }, { - label: '版本', + label: t('Version'), fieldname: 'version', width: 0.5, }, { - label: '到期时间', + label: t('Due Date'), fieldname: 'site_end_date', width: 1, format(value) { @@ -221,7 +222,7 @@ export default { ], primaryAction({ listResource: sites }) { return { - label: '新建站点', + label: t('New Site'), variant: 'solid', slots: { prefix: icon('plus'), @@ -234,7 +235,7 @@ export default { moreActions({ listResource: sites }) { return [ { - label: '导出为CSV', + label: t('Export as CSV'), icon: 'download', onClick() { const fields = [ @@ -278,16 +279,16 @@ export default { route: '/sites/:name', statusBadge({ documentResource: site }) { const statusMap = { - 'Active': '激活', - 'Inactive': '未激活', - 'Suspended': '已暂停', - 'Broken': '损坏', - 'Archived': '已归档', - 'Pending': '待处理', - 'Installing': '安装中', - 'Running': '运行中', - 'Success': '成功', - 'Failure': '失败' + 'Active': t('Active'), + 'Inactive': t('Inactive'), + 'Suspended': t('Suspended'), + 'Broken': t('Broken'), + 'Archived': t('Archived'), + 'Pending': t('Pending'), + 'Installing': t('Installing'), + 'Running': t('Running'), + 'Success': t('Success'), + 'Failure': t('Failure') }; return { label: statusMap[site.pg.status] || site.pg.status }; }, @@ -328,7 +329,7 @@ export default { }, tabs: [ { - label: '概览', + label: t('Overview'), icon: icon('home'), route: 'overview', type: 'Component', @@ -342,7 +343,7 @@ export default { }, getAppsTab(true), { - label: '域名', + label: t('Domain'), icon: icon('external-link'), route: 'domains', type: 'list', @@ -355,22 +356,22 @@ export default { }, columns: [ { - label: '域名', + label: t('Domain'), fieldname: 'domain', }, { - label: '状态', + label: t('Status'), fieldname: 'status', type: 'Badge', format(value) { const statusMap = { - 'Active': '激活', - 'Inactive': '未激活', - 'Suspended': '已暂停', - 'Broken': '损坏', - 'Archived': '已归档', - 'Pending': '待处理', - 'Installing': '安装中', + 'Active': t('Active'), + 'Inactive': t('Inactive'), + 'Suspended': t('Suspended'), + 'Broken': t('Broken'), + 'Archived': t('Archived'), + 'Pending': t('Pending'), + 'Installing': t('Installing'), 'Running': '运行中', 'Success': '成功', 'Failure': '失败' @@ -463,10 +464,10 @@ export default { domain: row.domain, }), { - loading: '正在移除域名...', + loading: t('Removing domain...'), success: () => { hide(); - return '域名已移除'; + return t('Domain removed'); }, error: (e) => getToastErrorMessage(e), }, @@ -476,12 +477,12 @@ export default { }, }, { - label: '设为主域名', + label: t('Set as Primary Domain'), condition: () => !row.primary && row.status === 'Active', onClick() { confirmDialog({ - title: `设为主域名`, - message: `您确定要将域名 ${row.domain} 设为站点 ${site.pg?.name} 的主域名吗?`, + title: t('Set as Primary Domain'), + message: t('Are you sure you want to set domain {domain} as the primary domain for site {site}?', { domain: row.domain, site: site.pg?.name }), onSuccess({ hide }) { if (site.setPrimaryDomain.loading) return; toast.promise( @@ -489,10 +490,10 @@ export default { domain: row.domain, }), { - loading: '正在设置主域名...', + loading: t('Setting primary domain...'), success: () => { hide(); - return '主域名已设置'; + return t('Primary domain set'); }, error: (e) => getToastErrorMessage(e), }, @@ -502,15 +503,15 @@ export default { }, }, { - label: '重定向到主域名', + label: t('Redirect to Primary Domain'), condition: () => !row.primary && !row.redirect_to_primary && row.status === 'Active', onClick() { confirmDialog({ - title: `重定向域名`, - message: `您确定要将域名 ${row.domain} 重定向到站点 ${site.pg?.name} 的主域名吗?`, + title: t('Redirect Domain'), + message: t('Are you sure you want to redirect domain {domain} to the primary domain of site {site}?', { domain: row.domain, site: site.pg?.name }), onSuccess({ hide }) { if (site.redirectToPrimary.loading) return; toast.promise( @@ -518,10 +519,10 @@ export default { domain: row.domain, }), { - loading: '正在重定向域名...', + loading: t('Redirecting domain...'), success: () => { hide(); - return '域名已重定向'; + return t('Domain redirected'); }, error: (e) => getToastErrorMessage(e), }, @@ -884,7 +885,7 @@ export default { }, getJobsTab('Site'), { - label: '操作', + label: t('Actions'), icon: icon('sliders'), route: 'actions', type: 'Component', @@ -895,7 +896,7 @@ export default { }, }, { - label: '更新', + label: t('Update'), icon: icon('arrow-up-circle'), route: 'updates', type: 'list', @@ -916,7 +917,7 @@ export default { ], columns: [ { - label: '类型', + label: t('Type'), fieldname: 'deploy_type', width: 0.3, format(value) { @@ -924,7 +925,7 @@ export default { } }, { - label: '状态', + label: t('Status'), fieldname: 'status', type: 'Badge', width: 0.5, @@ -933,7 +934,7 @@ export default { } }, // { - // label: '备份', + // label: t('Backup'), // width: 0.4, // type: 'Component', // component({ row }) { @@ -943,24 +944,24 @@ export default { // class: 'truncate text-base', // }, // row.skipped_backups - // ? '跳过' - // : row.backup_type || '逻辑', + // ? t('Skipped') + // : row.backup_type || t('Logical'), // ); // }, // }, { - label: '创建者', + label: t('Owner'), fieldname: 'owner', }, { - label: '计划时间', + label: t('Scheduled Time'), fieldname: 'scheduled_time', format(value) { return date(value, 'lll'); }, }, { - label: '更新时间', + label: t('Updated On'), fieldname: 'updated_on', format(value) { return date(value, 'lll'); @@ -970,7 +971,7 @@ export default { rowActions({ row, documentResource: site }) { return [ { - label: '编辑', + label: t('Update'), condition: () => row.status === 'Scheduled', onClick() { let SiteUpdateDialog = defineAsyncComponent( @@ -985,22 +986,22 @@ export default { }, }, { - label: '取消', + label: t('Cancel'), condition: () => row.status === 'Scheduled', onClick() { confirmDialog({ - title: '取消更新', - message: `您确定要取消计划的更新吗?`, + title: t('Cancel Update'), + message: t('Are you sure you want to cancel the scheduled update?'), onSuccess({ hide }) { if (site.cancelUpdate.loading) return; toast.promise( site.cancelUpdate.submit({ site_update: row.name }), { - loading: '正在取消更新...', + loading: t('Cancelling update...'), success: () => { hide(); site.reload(); - return '更新已取消'; + return t('Update cancelled'); }, error: (e) => getToastErrorMessage(e), }, @@ -1010,7 +1011,7 @@ export default { }, }, { - label: '查看任务', + label: t('View Job'), condition: () => row.status !== 'Scheduled', onClick() { router.push({ @@ -1020,7 +1021,7 @@ export default { }, }, { - label: '立即更新', + label: t('Update Now'), condition: () => row.status === 'Scheduled', onClick() { let siteUpdate = getDocResource({ @@ -1032,21 +1033,21 @@ export default { }); toast.promise(siteUpdate.updateNow.submit(), { - loading: '正在更新站点...', + loading: t('Updating site...'), success: () => { router.push({ name: 'Site Update', params: { id: row.name }, }); - return '站点更新已启动'; + return t('Site update started'); }, - error: '更新站点失败', + error: t('Failed to update site'), }); }, }, { - label: '查看应用更改', + label: t('View App Changes'), onClick() { createListResource({ pagetype: 'Deploy Candidate Difference App', @@ -1338,7 +1339,7 @@ export default { routes: [ { - name: '站点更新', + name: t('Site Update'), path: 'updates/:id', component: () => import('../pages/SiteUpdate.vue'), }, diff --git a/dashboard/src/pages/LoginSignup.vue b/dashboard/src/pages/LoginSignup.vue index dfae2c8..a39a5cc 100644 --- a/dashboard/src/pages/LoginSignup.vue +++ b/dashboard/src/pages/LoginSignup.vue @@ -944,7 +944,6 @@ export default { /[0-9]/.test(this.signupPassword); }, isPhoneNumberValid() { - // 中国大陆手机号验证:11位数字,以1开头,第二位为3-9 const phoneRegex = /^1[3-9]\d{9}$/; return phoneRegex.test(this.phoneNumber); }, diff --git a/dashboard/src/views/site/Sites.vue b/dashboard/src/views/site/Sites.vue index bf97be6..0e48a06 100644 --- a/dashboard/src/views/site/Sites.vue +++ b/dashboard/src/views/site/Sites.vue @@ -4,13 +4,13 @@
- +