feat: 本次提交更新内容如下

存一波
This commit is contained in:
笔记本里的永平
2025-07-19 14:17:05 +08:00
parent b128410346
commit 4af615889b
8 changed files with 6879 additions and 336 deletions

6402
nkebao/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -3,6 +3,7 @@
"version": "0.1.0",
"private": true,
"dependencies": {
"@ant-design/icons": "^5.6.1",
"antd": "^5.13.1",
"antd-mobile": "^5.39.1",
"axios": "^1.6.7",

View File

@@ -3,9 +3,9 @@ import { NavBar } from "antd-mobile";
import {
AppOutline,
UserOutline,
LoopOutline,
TravelOutline,
ClockCircleOutline,
SendOutline,
StarOutline,
} from "antd-mobile-icons";
import MeauMobile from "@/components/MeauMobile/MeauMoible";
import Layout from "@/components/Layout/Layout";
@@ -38,19 +38,21 @@ const Home: React.FC = () => {
{
label: "同步朋友圈",
value: res.momentsNum,
icon: <LoopOutline style={{ fontSize: 16, color: "#ff6b35" }} />,
icon: (
<ClockCircleOutline style={{ fontSize: 16, color: "#ff6b35" }} />
),
color: "#ff6b35",
},
{
label: "群发任务",
value: res.groupPushNum,
icon: <TravelOutline style={{ fontSize: 16, color: "#ffd700" }} />,
icon: <SendOutline style={{ fontSize: 16, color: "#ffd700" }} />,
color: "#ffd700",
},
{
label: "获客转化率",
value: res.passRate,
icon: <LoopOutline style={{ fontSize: 16, color: "#4caf50" }} />,
icon: <StarOutline style={{ fontSize: 16, color: "#4caf50" }} />,
color: "#4caf50",
},
{

View File

@@ -1,71 +1,150 @@
.home-page {
padding: 12px;
background: #f5f5f5;
.mine-page {
padding: 16px;
background-color: #f5f5f5;
min-height: 100vh;
}
.home-cards {
display: flex;
gap: 12px;
margin-bottom: 12px;
> :global(.adm-card) {
flex: 1;
.user-card {
margin-bottom: 16px;
border-radius: 12px;
overflow: hidden;
:global(.adm-card-body) {
padding: 20px;
}
}
.home-section {
margin-bottom: 12px;
background: #fff;
border-radius: 12px;
box-shadow: 0 2px 8px rgba(0,0,0,0.03);
}
.home-section-title {
font-size: 16px;
font-weight: 600;
margin-bottom: 15px;
padding-left: 16px;
}
.home-scene-stats {
.user-info {
display: flex;
justify-content: space-between;
margin: 0 8px 8px 8px;
align-items: center;
gap: 16px;
}
.home-scene-item {
.user-avatar {
width: 60px;
height: 60px;
border-radius: 50%;
overflow: hidden;
border: 2px solid var(--primary-color);
img {
width: 100%;
height: 100%;
object-fit: cover;
}
}
.user-details {
flex: 1;
text-align: center;
}
.home-scene-icon {
margin: 0 auto 4px auto;
}
.home-scene-value {
font-size: 18px;
font-weight: bold;
color: #1677ff;
}
.home-scene-label {
font-size: 12px;
color: #888;
}
.home-today-item {
text-align: center;
padding: 8px 0;
background: #f7f8fa;
.user-name {
font-size: 18px;
font-weight: 600;
color: #333;
margin-bottom: 4px;
}
.user-level {
font-size: 14px;
color: var(--primary-color);
margin-bottom: 4px;
}
.user-points {
font-size: 12px;
color: #666;
}
.menu-card {
margin-bottom: 16px;
border-radius: 12px;
overflow: hidden;
:global(.adm-card-body) {
padding: 0;
}
:global(.adm-list-item) {
padding: 16px;
border-bottom: 1px solid #f0f0f0;
&:last-child {
border-bottom: none;
}
:global(.adm-list-item-content) {
padding: 0;
}
:global(.adm-list-item-content-prefix) {
margin-right: 12px;
color: var(--primary-color);
font-size: 20px;
}
:global(.adm-list-item-content-main) {
flex: 1;
}
:global(.adm-list-item-title) {
font-size: 16px;
color: #333;
margin-bottom: 4px;
}
:global(.adm-list-item-description) {
font-size: 12px;
color: #666;
}
:global(.adm-list-item-content-arrow) {
color: #ccc;
}
}
}
.logout-section {
padding: 0 16px;
}
.logout-btn {
border-radius: 8px;
}
.home-today-value {
font-size: 18px;
font-weight: bold;
color: #1677ff;
}
.home-today-label {
font-size: 12px;
color: #888;
height: 48px;
font-size: 16px;
font-weight: 500;
}
.home-chart {
margin-top: 8px;
width: 100%;
min-height: 100px;
// 响应式设计
@media (max-width: 375px) {
.mine-page {
padding: 12px;
}
.user-info {
gap: 12px;
}
.user-avatar {
width: 50px;
height: 50px;
}
.user-name {
font-size: 16px;
}
.menu-card {
:global(.adm-list-item) {
padding: 12px;
:global(.adm-list-item-content-prefix) {
font-size: 18px;
}
:global(.adm-list-item-title) {
font-size: 14px;
}
}
}
}

View File

@@ -1,132 +1,143 @@
import React, { useState } from "react";
import { Card, NavBar, TabBar, Grid } from "antd-mobile";
import React from "react";
import { Card, NavBar, List, Button } from "antd-mobile";
import {
AppOutline,
UserOutline,
PieOutline, // 替换 BarChartOutline
ShopbagOutline,
AppOutline,
BellOutline,
HeartOutline,
StarOutline,
MessageOutline,
SendOutline,
MailOutline,
} from "antd-mobile-icons";
import MeauMobile from "@/components/MeauMobile/MeauMoible";
import Layout from "@/components/Layout/Layout";
import style from "./index.module.scss";
const sceneStats = [
{
label: "公众号获客",
value: 234,
icon: <ShopbagOutline style={{ fontSize: 28, color: "#4caf50" }} />,
},
{
label: "海报获客",
value: 167,
icon: <AppOutline style={{ fontSize: 28, color: "#ff9800" }} />,
},
{
label: "抖音获客",
value: 156,
icon: <PieOutline style={{ fontSize: 28, color: "#2196f3" }} />,
},
{
label: "小红书获客",
value: 89,
icon: <UserOutline style={{ fontSize: 28, color: "#e91e63" }} />,
},
];
const todayStats = [
{ label: "朋友圈同步", value: 12 },
{ label: "群发任务", value: 8 },
{ label: "获客转化", value: "85%" },
{ label: "系统活跃度", value: "98%" },
];
const Mine: React.FC = () => {
const userInfo = {
name: "张三",
avatar: "https://via.placeholder.com/60",
level: "VIP会员",
points: 1280,
};
const menuItems = [
{
icon: <UserOutline />,
title: "个人资料",
subtitle: "修改个人信息",
path: "/profile",
},
{
icon: <AppOutline />,
title: "系统设置",
subtitle: "应用设置与偏好",
path: "/settings",
},
{
icon: <BellOutline />,
title: "消息通知",
subtitle: "通知设置",
path: "/notifications",
},
{
icon: <HeartOutline />,
title: "我的收藏",
subtitle: "收藏的内容",
path: "/favorites",
},
{
icon: <StarOutline />,
title: "我的评价",
subtitle: "查看评价记录",
path: "/reviews",
},
{
icon: <MessageOutline />,
title: "意见反馈",
subtitle: "问题反馈与建议",
path: "/feedback",
},
{
icon: <SendOutline />,
title: "联系客服",
subtitle: "在线客服",
path: "/customer-service",
},
{
icon: <MailOutline />,
title: "关于我们",
subtitle: "版本信息",
path: "/about",
},
];
const Home: React.FC = () => {
return (
<Layout
header={
<NavBar back={null} style={{ background: "#fff" }}>
<span style={{ color: "#1677ff", fontWeight: 700 }}></span>
<div style={{ color: "var(--primary-color)", fontWeight: 600 }}>
</div>
</NavBar>
}
footer={<MeauMobile />}
>
<div className={style["home-page"]}>
{/* 统计卡片 */}
<div className={style["home-cards"]}>
<Card className={style["home-card"]}>
<div className={style["home-card-title"]}></div>
<div className={style["home-card-value"]}>0</div>
</Card>
<Card className={style["home-card"]}>
<div className={style["home-card-title"]}></div>
<div className={style["home-card-value"]}>0</div>
</Card>
<Card className={style["home-card"]}>
<div className={style["home-card-title"]}>线</div>
<div className={style["home-card-value"]}>0</div>
</Card>
</div>
{/* 场景获客统计 */}
<Card className={style["home-section"]}>
<div className={style["home-section-title"]}></div>
<div className={style["home-scene-stats"]}>
{sceneStats.map((item) => (
<div key={item.label} className={style["home-scene-item"]}>
<div className={style["home-scene-icon"]}>{item.icon}</div>
<div className={style["home-scene-value"]}>{item.value}</div>
<div className={style["home-scene-label"]}>{item.label}</div>
<div className={style["mine-page"]}>
{/* 用户信息卡片 */}
<Card className={style["user-card"]}>
<div className={style["user-info"]}>
<div className={style["user-avatar"]}>
<img src={userInfo.avatar} alt="头像" />
</div>
<div className={style["user-details"]}>
<div className={style["user-name"]}>{userInfo.name}</div>
<div className={style["user-level"]}>{userInfo.level}</div>
<div className={style["user-points"]}>
: {userInfo.points}
</div>
))}
</div>
</div>
</Card>
{/* 今日数据 */}
<Card className={style["home-section"]}>
<div className={style["home-section-title"]}></div>
<Grid columns={2} gap={12}>
{todayStats.map((item) => (
<Grid.Item key={item.label}>
<div className={style["home-today-item"]}>
<div className={style["home-today-value"]}>{item.value}</div>
<div className={style["home-today-label"]}>{item.label}</div>
</div>
</Grid.Item>
))}
</Grid>
</Card>
{/* 每日获客趋势(静态图表占位) */}
<Card className={style["home-section"]}>
<div className={style["home-section-title"]}></div>
<div className={style["home-chart"]}>
<svg width="100%" height="120">
<polyline
fill="none"
stroke="#1677ff"
strokeWidth="3"
points="10,100 40,80 70,60 100,50 130,40 160,60 190,80"
{/* 菜单列表 */}
<Card className={style["menu-card"]}>
<List>
{menuItems.map((item, index) => (
<List.Item
key={index}
prefix={item.icon}
title={item.title}
description={item.subtitle}
arrow
onClick={() => {
// 这里可以添加导航逻辑
console.log(`点击了: ${item.title}`);
}}
/>
{/* x轴文字 */}
{["周一", "周二", "周三", "周四", "周五", "周六", "周日"].map(
(d, i) => (
<text
key={d}
x={10 + i * 30}
y={115}
fontSize="12"
textAnchor="middle"
>
{d}
</text>
)
)}
</svg>
</div>
))}
</List>
</Card>
{/* 退出登录按钮 */}
<div className={style["logout-section"]}>
<Button
block
color="danger"
fill="outline"
className={style["logout-btn"]}
onClick={() => {
// 这里可以添加退出登录逻辑
console.log("退出登录");
}}
>
退
</Button>
</div>
</div>
</Layout>
);
};
export default Home;
export default Mine;

View File

@@ -31,9 +31,9 @@
// 页面容器
.scene-page {
padding: 12px;
background: #f5f5f5;
min-height: calc(100vh - 100px);
background: #f5f6fa;
min-height: 100vh;
padding: 0 0 60px 0;
}
// 错误提示
@@ -142,109 +142,89 @@
}
}
.scenarios-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 16px;
padding: 16px;
}
.scenario-card {
background: #fff;
border-radius: 12px;
padding: 16px 20px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
border: 1px solid #f0f0f0;
transition: all 0.2s ease;
display: flex;
justify-content: space-between;
align-items: center;
min-height: 78px;
border-radius: 16px;
box-shadow: 0 2px 8px rgba(0,0,0,0.06);
transition: box-shadow 0.2s, transform 0.2s;
cursor: pointer;
overflow: hidden;
&:hover {
box-shadow: 0 4px 16px rgba(24, 142, 238, 0.12);
border-color: var(--primary-color-light);
box-shadow: 0 6px 16px rgba(0,0,0,0.12);
transform: translateY(-2px) scale(1.02);
}
}
.scenario-left {
.card-inner {
display: flex;
flex-direction: column;
align-items: center;
gap: 16px;
flex: 1;
padding: 18px 10px 14px 10px;
}
.scenario-icon {
width: 52px;
height: 52px;
background: linear-gradient(135deg, #f0f8ff 0%, #e6f7ff 100%);
border-radius: 12px;
.card-img-wrap {
margin-bottom: 8px;
}
.card-img-bg {
width: 48px;
height: 48px;
background: #f0f2f5;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
border: 1px solid rgba(24, 142, 238, 0.1);
flex-shrink: 0;
}
.scenario-image {
.card-img {
width: 32px;
height: 32px;
object-fit: contain;
border-radius: 8px;
}
.scenario-info {
display: flex;
flex-direction: column;
gap: 6px;
flex: 1;
min-width: 0;
}
.scenario-name {
.card-title {
font-size: 16px;
font-weight: 600;
color: #1a1a1a;
line-height: 1.3;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
color: #1677ff;
text-align: center;
margin-bottom: 2px;
}
.scenario-stats {
.card-desc {
font-size: 12px;
color: #888;
text-align: center;
margin-bottom: 6px;
line-height: 1.4;
min-height: 32px;
max-height: 32px;
overflow: hidden;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
}
.card-stats {
display: flex;
align-items: center;
justify-content: center;
gap: 10px;
margin-top: 4px;
}
.stat-text {
.card-count {
font-size: 13px;
color: #666;
font-weight: 500;
}
.scenario-right {
display: flex;
flex-direction: column;
align-items: flex-end;
gap: 8px;
flex-shrink: 0;
}
.scenario-status {
display: flex;
justify-content: flex-end;
}
.status-tag {
font-size: 11px;
padding: 3px 8px;
border-radius: 10px;
font-weight: 500;
}
.scenario-growth {
.card-growth {
font-size: 12px;
color: #52c41a;
display: flex;
align-items: center;
gap: 3px;
font-size: 16px;
font-weight: 700;
}
.growth-icon {
font-size: 14px;
}
// 空状态
@@ -317,3 +297,35 @@
font-size: 13px;
}
}
@media (max-width: 500px) {
.scenarios-grid {
gap: 10px;
padding: 10px;
}
.card-inner {
padding: 12px 4px 10px 4px;
}
.card-img-bg {
width: 40px;
height: 40px;
}
.card-img {
width: 26px;
height: 26px;
}
.card-title {
font-size: 15px;
}
.card-desc {
font-size: 11px;
min-height: 24px;
max-height: 24px;
}
.card-count {
font-size: 12px;
}
.card-growth {
font-size: 11px;
}
}

View File

@@ -1,7 +1,8 @@
import React, { useEffect, useState } from "react";
import { useNavigate } from "react-router-dom";
import { NavBar, Button, SpinLoading, Toast, Tag } from "antd-mobile";
import { AddOutline, UserAddOutline } from "antd-mobile-icons";
import { NavBar, Button, Toast, SpinLoading } from "antd-mobile";
import { AddOutline } from "antd-mobile-icons";
import { RiseOutlined } from "@ant-design/icons";
import MeauMobile from "@/components/MeauMobile/MeauMoible";
import Layout from "@/components/Layout/Layout";
import { getScenarios } from "./api";
@@ -11,11 +12,23 @@ interface Scenario {
id: string;
name: string;
image: string;
description?: string;
count: number;
growth: string;
status: number;
}
const scenarioDescriptions: Record<string, string> = {
douyin: "通过抖音平台进行精准获客",
xiaohongshu: "利用小红书平台进行内容营销获客",
gongzhonghao: "通过微信公众号进行获客",
haibao: "通过海报分享进行获客",
phone: "通过电话营销进行获客",
weixinqun: "通过微信群进行获客",
payment: "通过付款码进行获客",
api: "通过API接口进行获客",
};
const Scene: React.FC = () => {
const navigate = useNavigate();
const [scenarios, setScenarios] = useState<Scenario[]>([]);
@@ -26,25 +39,23 @@ const Scene: React.FC = () => {
const fetchScenarios = async () => {
setLoading(true);
setError("");
try {
const response = await getScenarios({ page: 1, limit: 20 });
// 转换API数据为前端需要的格式
const transformedScenarios: Scenario[] = response.map((item: any) => ({
id: item.id.toString(),
name: item.name,
image:
item.image ||
"https://hebbkx1anhila5yf.public.blob.vercel-storage.com/image-api.png",
count: item.count, // 模拟今日数据
growth: item.growth, // 模拟增长率
description:
scenarioDescriptions[item.name?.toLowerCase()] ||
"通过该平台进行获客",
count: item.count,
growth: item.growth,
status: item.status,
}));
setScenarios(transformedScenarios);
} catch (error) {
console.error("获取场景数据失败:", error);
setError("获取场景数据失败,请稍后重试");
Toast.show({
content: "获取场景数据失败,请稍后重试",
@@ -54,7 +65,6 @@ const Scene: React.FC = () => {
setLoading(false);
}
};
fetchScenarios();
}, []);
@@ -68,12 +78,61 @@ const Scene: React.FC = () => {
navigate("/scenarios/new");
};
const getGrowthColor = (growth: string) => {
const value = parseFloat(growth.replace(/[+%]/g, ""));
if (value > 10) return "#52c41a";
if (value > 5) return "#faad14";
return "#ff4d4f";
};
if (loading) {
return (
<Layout
header={
<NavBar back={null} style={{ background: "#fff" }}>
<div className={style["nav-title"]}></div>
<Button
size="small"
color="primary"
onClick={handleNewPlan}
className={style["new-plan-btn"]}
style={{ marginLeft: "auto" }}
>
<AddOutline />
</Button>
</NavBar>
}
footer={<MeauMobile />}
>
<div className={style["loading"]}>
<SpinLoading color="primary" style={{ fontSize: 32 }} />
<div className={style["loading-text"]}>...</div>
</div>
</Layout>
);
}
if (error && scenarios.length === 0) {
return (
<Layout
header={
<NavBar back={null} style={{ background: "#fff" }}>
<div className={style["nav-title"]}></div>
<Button
size="small"
color="primary"
onClick={handleNewPlan}
className={style["new-plan-btn"]}
style={{ marginLeft: "auto" }}
>
<AddOutline />
</Button>
</NavBar>
}
footer={<MeauMobile />}
>
<div className={style["error"]}>
<div className={style["error-text"]}>{error}</div>
<Button color="primary" onClick={() => window.location.reload()}>
</Button>
</div>
</Layout>
);
}
return (
<Layout
@@ -88,94 +147,58 @@ const Scene: React.FC = () => {
color="primary"
onClick={handleNewPlan}
className={style["new-plan-btn"]}
style={{ marginLeft: "auto" }}
>
<AddOutline />
<span className={style["nav-right"]}></span>
<AddOutline />
</Button>
}
/>
></NavBar>
}
footer={<MeauMobile />}
>
<div className={style["scene-page"]}>
<div className={style["scene-header"]}>
<div className={style["header-title"]}>
<UserAddOutline className={style["header-icon"]} />
<span></span>
</div>
<div className={style["header-subtitle"]}>
</div>
</div>
<div className={style["scenarios-list"]}>
<div className={style["scenarios-grid"]}>
{scenarios.map((scenario) => (
<div
key={scenario.id}
className={style["scenario-item"]}
className={style["scenario-card"]}
onClick={() => handleScenarioClick(scenario.id, scenario.name)}
>
<div className={style["scenario-card"]}>
<div className={style["scenario-left"]}>
<div className={style["scenario-icon"]}>
<div className={style["card-inner"]}>
<div className={style["card-img-wrap"]}>
<div className={style["card-img-bg"]}>
<img
src={scenario.image}
alt={scenario.name}
className={style["scenario-image"]}
className={style["card-img"]}
onError={(e) => {
e.currentTarget.src =
"https://hebbkx1anhila5yf.public.blob.vercel-storage.com/image-api.png";
}}
/>
</div>
<div className={style["scenario-info"]}>
<div className={style["scenario-name"]}>
{scenario.name}
</div>
<div className={style["scenario-stats"]}>
<span className={style["stat-text"]}>
: {scenario.count}
</span>
</div>
</div>
</div>
<div className={style["scenario-right"]}>
<div className={style["scenario-status"]}>
<Tag
color={scenario.status === 1 ? "success" : "default"}
fill="outline"
className={style["status-tag"]}
>
{scenario.status === 1 ? "活跃" : "暂停"}
</Tag>
<div className={style["card-title"]}>{scenario.name}</div>
{scenario.description && (
<div className={style["card-desc"]}>
{scenario.description}
</div>
<div
className={style["scenario-growth"]}
style={{ color: getGrowthColor(scenario.growth) }}
>
)}
<div className={style["card-stats"]}>
<span className={style["card-count"]}>
: {scenario.count}
</span>
<span className={style["card-growth"]}>
<RiseOutlined
style={{ fontSize: 14, color: "#52c41a", marginRight: 2 }}
/>
{scenario.growth}
</div>
</span>
</div>
</div>
</div>
))}
</div>
{scenarios.length === 0 && !loading && (
<div className={style["empty-state"]}>
<div className={style["empty-icon"]}>📊</div>
<div className={style["empty-text"]}></div>
<Button
color="primary"
size="small"
onClick={handleNewPlan}
className={style["empty-action"]}
>
</Button>
</div>
)}
</div>
</Layout>
);

View File

@@ -1,15 +1,14 @@
import React from "react";
import { Link } from "react-router-dom";
import { Card, NavBar, Badge, Progress } from "antd-mobile";
import { Card, NavBar, Badge } from "antd-mobile";
import {
ThumbsUpOutline,
LikeOutline,
MessageOutline,
SendOutline,
TeamOutline,
ShareOutline,
LinkOutline,
AppOutline,
BarChartOutline,
LineChartOutline,
PieOutline,
ClockCircleOutline,
} from "antd-mobile-icons";
import Layout from "@/components/Layout/Layout";
@@ -33,7 +32,7 @@ const Workspace: React.FC = () => {
name: "自动点赞",
description: "智能自动点赞互动",
icon: (
<ThumbsUpOutline className={styles.icon} style={{ color: "#ff4d4f" }} />
<LikeOutline className={styles.icon} style={{ color: "#ff4d4f" }} />
),
path: "/workspace/auto-like",
bgColor: "#fff2f0",
@@ -77,7 +76,7 @@ const Workspace: React.FC = () => {
name: "流量分发",
description: "管理流量分发和分配",
icon: (
<ShareOutline className={styles.icon} style={{ color: "#1890ff" }} />
<LinkOutline className={styles.icon} style={{ color: "#1890ff" }} />
),
path: "/workspace/traffic-distribution",
bgColor: "#e6f7ff",
@@ -101,9 +100,7 @@ const Workspace: React.FC = () => {
id: "ai-analyzer",
name: "AI数据分析",
description: "智能分析客户行为特征",
icon: (
<BarChartOutline className={styles.icon} style={{ color: "#531dab" }} />
),
icon: <PieOutline className={styles.icon} style={{ color: "#531dab" }} />,
path: "/workspace/ai-analyzer",
bgColor: "#f0f0ff",
isNew: true,
@@ -121,17 +118,17 @@ const Workspace: React.FC = () => {
id: "ai-forecast",
name: "AI销售预测",
description: "智能预测销售趋势",
icon: (
<LineChartOutline
className={styles.icon}
style={{ color: "#d48806" }}
/>
),
icon: <PieOutline className={styles.icon} style={{ color: "#d48806" }} />,
path: "/workspace/ai-forecast",
bgColor: "#fffbe6",
},
];
// 进度条宽度
const progressPercent = Math.round(
(taskStats.inProgress / taskStats.total) * 100
);
return (
<Layout
header={
@@ -149,10 +146,26 @@ const Workspace: React.FC = () => {
<Card className={styles.statsCard}>
<div className={styles.statsTitle}></div>
<div className={styles.statsValue}>{taskStats.total}</div>
<Progress
percent={(taskStats.inProgress / taskStats.total) * 100}
className={styles.progress}
/>
<div className={styles.progress}>
<div
style={{
width: progressPercent + "%",
height: 6,
background: "var(--primary-color)",
borderRadius: 4,
transition: "width 0.3s",
}}
/>
<div
style={{
width: 100 - progressPercent + "%",
height: 6,
background: "#f0f0f0",
borderRadius: 4,
display: "inline-block",
}}
/>
</div>
<div className={styles.statsSubtitle}>
: {taskStats.inProgress} / : {taskStats.completed}
</div>