fix(web): disable unsupported subscription api calls
This commit is contained in:
@@ -4,7 +4,18 @@
|
||||
import React from 'react';
|
||||
import { Card, Progress, Row, Col, Tag, Space } from 'antd';
|
||||
import { ProjectOutlined, CloudOutlined, WarningOutlined } from '@ant-design/icons';
|
||||
import type { QuotaStatus } from '@/api/subscription';
|
||||
interface QuotaStatus {
|
||||
projects: {
|
||||
used: number;
|
||||
limit: number;
|
||||
status: string;
|
||||
};
|
||||
storage: {
|
||||
used_gb: number;
|
||||
limit_gb: number;
|
||||
status: string;
|
||||
};
|
||||
}
|
||||
|
||||
interface QuotaDisplayProps {
|
||||
quota: QuotaStatus;
|
||||
|
||||
@@ -1,140 +1,30 @@
|
||||
/**
|
||||
* 订阅升级流程页面
|
||||
*/
|
||||
import React, { useState } from 'react';
|
||||
import { useParams, useNavigate } from 'react-router-dom';
|
||||
import { Card, Steps, Button, Result, Descriptions, message } from 'antd';
|
||||
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
import { upgradeSubscription } from '@/api/subscription';
|
||||
import React from 'react';
|
||||
import { Button, Card, Result } from 'antd';
|
||||
import { useNavigate, useParams } from 'react-router-dom';
|
||||
|
||||
const UpgradeSubscription: React.FC = () => {
|
||||
const { workspaceId } = useParams<{ workspaceId: string }>();
|
||||
const navigate = useNavigate();
|
||||
const queryClient = useQueryClient();
|
||||
const [current, setCurrent] = useState(0);
|
||||
const [selectedPlan, setSelectedPlan] = useState<'pro' | 'enterprise'>('pro');
|
||||
|
||||
const upgradeMutation = useMutation({
|
||||
mutationFn: () => upgradeSubscription(workspaceId!, selectedPlan),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['workspace', workspaceId] });
|
||||
setCurrent(2);
|
||||
message.success('订阅升级成功!');
|
||||
},
|
||||
onError: (error: any) => {
|
||||
message.error(error.response?.data?.message || '升级失败');
|
||||
},
|
||||
});
|
||||
|
||||
const steps = [
|
||||
{
|
||||
title: '选择套餐',
|
||||
content: (
|
||||
<div>
|
||||
<Card
|
||||
style={{ marginBottom: 16, cursor: 'pointer' }}
|
||||
onClick={() => setSelectedPlan('pro')}
|
||||
className={selectedPlan === 'pro' ? 'selected-plan' : ''}
|
||||
>
|
||||
<h3>Pro 套餐</h3>
|
||||
<p>¥99/月</p>
|
||||
<ul>
|
||||
<li>无限项目</li>
|
||||
<li>100GB 存储</li>
|
||||
<li>20 个成员</li>
|
||||
</ul>
|
||||
</Card>
|
||||
<Card
|
||||
style={{ cursor: 'pointer' }}
|
||||
onClick={() => setSelectedPlan('enterprise')}
|
||||
className={selectedPlan === 'enterprise' ? 'selected-plan' : ''}
|
||||
>
|
||||
<h3>Enterprise 套餐</h3>
|
||||
<p>¥999/月</p>
|
||||
<ul>
|
||||
<li>无限项目</li>
|
||||
<li>1TB 存储</li>
|
||||
<li>无限成员</li>
|
||||
<li>专属支持</li>
|
||||
</ul>
|
||||
</Card>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: '确认订单',
|
||||
content: (
|
||||
<Card>
|
||||
<Descriptions column={1}>
|
||||
<Descriptions.Item label="套餐">
|
||||
{selectedPlan === 'pro' ? 'Pro' : 'Enterprise'}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="价格">
|
||||
{selectedPlan === 'pro' ? '¥99' : '¥999'} / 月
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="支付方式">
|
||||
在线支付(待接入)
|
||||
</Descriptions.Item>
|
||||
</Descriptions>
|
||||
</Card>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: '完成',
|
||||
content: (
|
||||
<Result
|
||||
status="success"
|
||||
title="升级成功!"
|
||||
subTitle="您的订阅已升级,新配额已生效。"
|
||||
extra={[
|
||||
<Button
|
||||
type="primary"
|
||||
key="workspace"
|
||||
onClick={() => navigate(`/workspaces/${workspaceId}`)}
|
||||
>
|
||||
返回工作空间
|
||||
</Button>,
|
||||
]}
|
||||
/>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
const next = () => {
|
||||
if (current === 0) {
|
||||
setCurrent(current + 1);
|
||||
} else if (current === 1) {
|
||||
upgradeMutation.mutate();
|
||||
}
|
||||
};
|
||||
|
||||
const prev = () => {
|
||||
setCurrent(current - 1);
|
||||
};
|
||||
|
||||
return (
|
||||
<div style={{ padding: '24px' }}>
|
||||
<h1>升级订阅</h1>
|
||||
<Steps current={current} items={steps} style={{ marginBottom: 24 }} />
|
||||
<div>{steps[current].content}</div>
|
||||
<div style={{ marginTop: 24 }}>
|
||||
{current < steps.length - 1 && (
|
||||
<>
|
||||
{current > 0 && (
|
||||
<Button style={{ marginRight: 8 }} onClick={prev}>
|
||||
上一步
|
||||
<Card>
|
||||
<Result
|
||||
status="info"
|
||||
title="订阅升级暂未开放"
|
||||
subTitle="当前版本未接入订阅、支付和配额变更后端服务。为避免假成功或调用不存在的接口,此入口已暂时关闭。"
|
||||
extra={[
|
||||
<Button key="plans" onClick={() => navigate('/subscription')}>
|
||||
查看套餐说明
|
||||
</Button>,
|
||||
workspaceId ? (
|
||||
<Button key="workspace" type="primary" onClick={() => navigate(`/workspaces/${workspaceId}`)}>
|
||||
返回工作空间
|
||||
</Button>
|
||||
)}
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={next}
|
||||
loading={upgradeMutation.isPending}
|
||||
>
|
||||
{current === 1 ? '确认升级' : '下一步'}
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
) : null,
|
||||
]}
|
||||
/>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -121,6 +121,19 @@ def test_web_dockerfile_uses_reachable_base_image_mirror():
|
||||
assert "FROM nginx:alpine" not in dockerfile
|
||||
|
||||
|
||||
def test_subscription_api_is_not_wired_to_ui_until_backend_exists():
|
||||
source_files = list(Path("apps/web/src").rglob("*.ts")) + list(Path("apps/web/src").rglob("*.tsx"))
|
||||
importers = []
|
||||
for path in source_files:
|
||||
if path.as_posix().endswith("apps/web/src/api/subscription.ts"):
|
||||
continue
|
||||
text = path.read_text(encoding="utf-8")
|
||||
if "@/api/subscription" in text or "../api/subscription" in text:
|
||||
importers.append(path.as_posix())
|
||||
|
||||
assert importers == []
|
||||
|
||||
|
||||
def test_backup_postgres_writes_manifest_and_version():
|
||||
script = Path("scripts/backup_postgres.sh").read_text(encoding="utf-8")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user