diff --git a/backend/crmeb-admin/src/main/java/com/zbkj/admin/config/WebSecurityConfig.java b/backend/crmeb-admin/src/main/java/com/zbkj/admin/config/WebSecurityConfig.java index 0ab04c6..8b139c7 100644 --- a/backend/crmeb-admin/src/main/java/com/zbkj/admin/config/WebSecurityConfig.java +++ b/backend/crmeb-admin/src/main/java/com/zbkj/admin/config/WebSecurityConfig.java @@ -148,6 +148,8 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter { .antMatchers("/api/admin/store/product/copy/**").permitAll() .antMatchers("/api/admin/merchandise/select").permitAll() .antMatchers("/api/admin/merchandise/update").permitAll() + // 老板驾驶舱独立 H5 页面接口,本机演示和报表归档使用 + .antMatchers("/api/admin/dashboard/**").permitAll() // 积分模块外部免认证只读接口(供 /integral-external/* 页面调用) .antMatchers("/api/external/integral/**").permitAll() // 除上面外的所有请求全部需要鉴权认证 diff --git a/backend/crmeb-admin/src/main/java/com/zbkj/admin/controller/BossDashboardController.java b/backend/crmeb-admin/src/main/java/com/zbkj/admin/controller/BossDashboardController.java new file mode 100644 index 0000000..1fb4748 --- /dev/null +++ b/backend/crmeb-admin/src/main/java/com/zbkj/admin/controller/BossDashboardController.java @@ -0,0 +1,50 @@ +package com.zbkj.admin.controller; + +import com.zbkj.common.response.dashboard.BossDashboardResponse; +import com.zbkj.common.result.CommonResult; +import com.zbkj.service.service.BossDashboardService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.nio.charset.StandardCharsets; + +/** + * 老板经营驾驶舱 + */ +@Slf4j +@RestController +@RequestMapping("api/admin/dashboard") +@Api(tags = "老板经营驾驶舱") +public class BossDashboardController { + + @Autowired + private BossDashboardService bossDashboardService; + + @ApiOperation(value = "老板驾驶舱概览") + @RequestMapping(value = "/overview", method = RequestMethod.GET) + public CommonResult overview(@RequestParam(value = "date", required = false) String date) { + return CommonResult.success(bossDashboardService.overview(date)); + } + + @ApiOperation(value = "生成经营日报归档 HTML") + @RequestMapping(value = "/daily-report/archive", method = RequestMethod.GET) + public ResponseEntity dailyReportArchive(@RequestParam(value = "date", required = false) String date) { + BossDashboardResponse overview = bossDashboardService.overview(date); + String html = bossDashboardService.dailyReportArchiveHtml(date); + String filename = "dashboard-daily-report-" + overview.getBusinessDate() + ".html"; + + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(new MediaType("text", "html", StandardCharsets.UTF_8)); + headers.add(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + filename + "\""); + return ResponseEntity.ok().headers(headers).body(html.getBytes(StandardCharsets.UTF_8)); + } +} diff --git a/backend/crmeb-common/src/main/java/com/zbkj/common/response/dashboard/BossDashboardResponse.java b/backend/crmeb-common/src/main/java/com/zbkj/common/response/dashboard/BossDashboardResponse.java new file mode 100644 index 0000000..2f3ac43 --- /dev/null +++ b/backend/crmeb-common/src/main/java/com/zbkj/common/response/dashboard/BossDashboardResponse.java @@ -0,0 +1,106 @@ +package com.zbkj.common.response.dashboard; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.List; + +/** + * 老板驾驶舱响应对象 + */ +@Data +@ApiModel(value = "BossDashboardResponse", description = "老板驾驶舱响应对象") +public class BossDashboardResponse { + + @ApiModelProperty(value = "业务日期") + private String businessDate; + + @ApiModelProperty(value = "生成时间") + private String generatedAt; + + @ApiModelProperty(value = "经营摘要") + private String summary; + + @ApiModelProperty(value = "核心指标") + private List kpis = new ArrayList<>(); + + @ApiModelProperty(value = "资金池指标") + private List fundPool = new ArrayList<>(); + + @ApiModelProperty(value = "今日节点快报") + private List snapshots = new ArrayList<>(); + + @ApiModelProperty(value = "近 7 天趋势") + private List trends = new ArrayList<>(); + + @ApiModelProperty(value = "高价值用户排行") + private List userRanks = new ArrayList<>(); + + @ApiModelProperty(value = "团队贡献排行") + private List teamRanks = new ArrayList<>(); + + @ApiModelProperty(value = "高货值未成交商品排行") + private List productRanks = new ArrayList<>(); + + @ApiModelProperty(value = "风险预警") + private List risks = new ArrayList<>(); + + @Data + public static class KpiMetric { + private String key; + private String title; + private Object value; + private String unit; + private String trendLabel; + private BigDecimal trendValue; + private String status = "normal"; + private Boolean featured = false; + } + + @Data + public static class TodaySnapshot { + private String slot; + private String title; + private String status; + private String generatedAt; + private String message; + private Integer purchaseUsers = 0; + private Integer orderCount = 0; + private BigDecimal dealAmount = BigDecimal.ZERO; + private BigDecimal paidAmount = BigDecimal.ZERO; + private Integer newMerchandiseCount = 0; + private BigDecimal selfBonusChange = BigDecimal.ZERO; + private BigDecimal shareBonusChange = BigDecimal.ZERO; + } + + @Data + public static class TrendPoint { + private String date; + private BigDecimal amount = BigDecimal.ZERO; + private Integer orders = 0; + private Integer newUsers = 0; + private BigDecimal bonus = BigDecimal.ZERO; + } + + @Data + public static class RankItem { + private String id; + private String name; + private BigDecimal value = BigDecimal.ZERO; + private String description; + private String badge; + } + + @Data + public static class RiskAlert { + private String id; + private String level; + private String type; + private String title; + private String description; + private String discoveredAt; + } +} diff --git a/backend/crmeb-service/src/main/java/com/zbkj/service/service/BossDashboardService.java b/backend/crmeb-service/src/main/java/com/zbkj/service/service/BossDashboardService.java new file mode 100644 index 0000000..70137be --- /dev/null +++ b/backend/crmeb-service/src/main/java/com/zbkj/service/service/BossDashboardService.java @@ -0,0 +1,25 @@ +package com.zbkj.service.service; + +import com.zbkj.common.response.dashboard.BossDashboardResponse; + +/** + * 老板经营驾驶舱服务 + */ +public interface BossDashboardService { + + /** + * 获取老板经营驾驶舱数据 + * + * @param date 业务日期,格式 yyyy-MM-dd,为空时默认上一个工作日 + * @return BossDashboardResponse + */ + BossDashboardResponse overview(String date); + + /** + * 生成经营日报归档 HTML + * + * @param date 业务日期,格式 yyyy-MM-dd,为空时默认上一个工作日 + * @return standalone HTML + */ + String dailyReportArchiveHtml(String date); +} diff --git a/backend/crmeb-service/src/main/java/com/zbkj/service/service/impl/BossDashboardServiceImpl.java b/backend/crmeb-service/src/main/java/com/zbkj/service/service/impl/BossDashboardServiceImpl.java new file mode 100644 index 0000000..cf2916f --- /dev/null +++ b/backend/crmeb-service/src/main/java/com/zbkj/service/service/impl/BossDashboardServiceImpl.java @@ -0,0 +1,564 @@ +package com.zbkj.service.service.impl; + +import cn.hutool.core.date.DateTime; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.zbkj.common.model.consignment.WaMerchandise; +import com.zbkj.common.model.consignment.WaOrder; +import com.zbkj.common.model.consignment.WaSelfbonusLog; +import com.zbkj.common.model.consignment.WaSharebonusLog; +import com.zbkj.common.model.consignment.WaUsers; +import com.zbkj.common.model.consignment.WaWithdraw; +import com.zbkj.common.response.dashboard.BossDashboardResponse; +import com.zbkj.service.dao.consignment.WaMerchandiseDao; +import com.zbkj.service.dao.consignment.WaOrderDao; +import com.zbkj.service.dao.consignment.WaSelfbonusLogDao; +import com.zbkj.service.dao.consignment.WaSharebonusLogDao; +import com.zbkj.service.dao.consignment.WaUsersDao; +import com.zbkj.service.dao.consignment.WaWithdrawDao; +import com.zbkj.service.service.BossDashboardService; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.Calendar; +import java.util.Date; +import java.util.List; +import java.util.Map; + +/** + * 老板经营驾驶舱服务实现 + */ +@Service +public class BossDashboardServiceImpl implements BossDashboardService { + + @Resource + private WaOrderDao waOrderDao; + + @Resource + private WaMerchandiseDao waMerchandiseDao; + + @Resource + private WaUsersDao waUsersDao; + + @Resource + private WaSelfbonusLogDao waSelfbonusLogDao; + + @Resource + private WaSharebonusLogDao waSharebonusLogDao; + + @Resource + private WaWithdrawDao waWithdrawDao; + + @Override + public BossDashboardResponse overview(String date) { + DateTime businessDate = StrUtil.isBlank(date) ? previousWorkday(DateUtil.date()) : DateUtil.parseDate(date); + DateTime previousDate = previousWorkday(businessDate); + DateRange businessRange = dayRange(businessDate); + DateRange previousRange = dayRange(previousDate); + + DailyMetrics metrics = buildDailyMetrics(businessRange); + DailyMetrics previousMetrics = buildDailyMetrics(previousRange); + + BossDashboardResponse response = new BossDashboardResponse(); + response.setBusinessDate(businessDate.toString("yyyy-MM-dd")); + response.setGeneratedAt(DateUtil.formatDateTime(new Date())); + response.setSummary(buildSummary(metrics)); + response.getKpis().add(metric("dealAmount", "上个工作日成交额", metrics.dealAmount, "元", "较上一工作日", ratio(metrics.dealAmount, previousMetrics.dealAmount), statusByRatio(metrics.dealAmount, previousMetrics.dealAmount), true)); + response.getKpis().add(metric("orderCount", "上个工作日订单数", metrics.orderCount, "单", "较上一工作日", ratio(metrics.orderCount, previousMetrics.orderCount), statusByRatio(metrics.orderCount, previousMetrics.orderCount), false)); + response.getKpis().add(metric("purchaseUsers", "采购用户", metrics.purchaseUsers, "人", "较上一工作日", ratio(metrics.purchaseUsers, previousMetrics.purchaseUsers), statusByRatio(metrics.purchaseUsers, previousMetrics.purchaseUsers), false)); + response.getKpis().add(metric("newUsers", "新增用户", metrics.newUsers, "人", "较上一工作日", ratio(metrics.newUsers, previousMetrics.newUsers), statusByRatio(metrics.newUsers, previousMetrics.newUsers), false)); + response.getKpis().add(metric("newMerchandise", "新增寄售商品", metrics.newMerchandiseCount, "件", "较上一工作日", ratio(metrics.newMerchandiseCount, previousMetrics.newMerchandiseCount), statusByRatio(metrics.newMerchandiseCount, previousMetrics.newMerchandiseCount), false)); + response.getKpis().add(metric("selfBonus", "个人奖金发放", metrics.selfBonus, "元", "较上一工作日", ratio(metrics.selfBonus, previousMetrics.selfBonus), "normal", false)); + response.getKpis().add(metric("shareBonus", "推广奖金发放", metrics.shareBonus, "元", "较上一工作日", ratio(metrics.shareBonus, previousMetrics.shareBonus), "normal", false)); + response.getKpis().add(metric("pendingAmount", "待支付/待结算", metrics.pendingAmount, "元", "需关注", null, metrics.pendingAmount.compareTo(BigDecimal.ZERO) > 0 ? "warning" : "normal", false)); + + buildFundPool(response); + buildSnapshots(response); + buildTrends(response, businessDate); + buildRanks(response); + buildRisks(response); + return response; + } + + @Override + public String dailyReportArchiveHtml(String date) { + BossDashboardResponse data = overview(date); + StringBuilder html = new StringBuilder(); + html.append(""); + html.append(""); + html.append("经营日报归档 - ").append(escape(data.getBusinessDate())).append(""); + html.append("
"); + html.append("

Daily Report Archive

经营日报归档

"); + html.append("

").append(escape(data.getSummary())).append("

"); + html.append("数据日期:").append(escape(data.getBusinessDate())).append(""); + html.append("生成时间:").append(escape(data.getGeneratedAt())).append(""); + html.append("归档类型:Standalone HTML
"); + appendMetricsSection(html, "核心经营指标", data.getKpis()); + appendTrendSection(html, data); + appendMetricsSection(html, "资金池摘要", data.getFundPool()); + appendRankSection(html, "高价值用户", data.getUserRanks()); + appendRankSection(html, "团队贡献排行", data.getTeamRanks()); + appendRankSection(html, "高货值未成交商品", data.getProductRanks()); + appendRiskSection(html, data); + html.append("

本归档由经营驾驶舱实时数据生成,可独立保存和打开。

"); + html.append("
"); + return html.toString(); + } + + private DailyMetrics buildDailyMetrics(DateRange range) { + DailyMetrics metrics = new DailyMetrics(); + metrics.dealAmount = sumOrderAmount(range.start, range.end, true, null); + metrics.orderCount = countOrders(range.start, range.end, null); + metrics.purchaseUsers = distinctBuyerCount(range.start, range.end, null); + metrics.newUsers = countUsers(range.start, range.end); + metrics.newMerchandiseCount = countMerchandise(range.start, range.end); + metrics.selfBonus = sumSelfBonus(range.start, range.end); + metrics.shareBonus = sumShareBonus(range.start, range.end); + metrics.pendingAmount = sumOrderAmount(range.start, range.end, false, null); + return metrics; + } + + private void buildFundPool(BossDashboardResponse response) { + BigDecimal money = sumUsersDecimal("money"); + BigDecimal coupon = sumUsersDecimal("coupon"); + BigDecimal selfBonus = sumUsersDecimal("self_bonus"); + BigDecimal shareBonus = sumUsersDecimal("share_bonus"); + BigDecimal score = sumUsersDecimal("score"); + BigDecimal pendingWithdraw = sumWithdrawAmount(0); + Integer pendingWithdrawCount = countWithdraw(0); + + response.getFundPool().add(metric("balance", "余额总额", money, "元", null, null, "normal", false)); + response.getFundPool().add(metric("coupon", "优惠券总额", coupon, "元", null, null, "normal", false)); + response.getFundPool().add(metric("selfBonusPool", "个人奖金总额", selfBonus, "元", null, null, selfBonus.compareTo(BigDecimal.ZERO) > 0 ? "warning" : "normal", false)); + response.getFundPool().add(metric("shareBonusPool", "推广奖金总额", shareBonus, "元", null, null, "normal", false)); + response.getFundPool().add(metric("integral", "积分总额", score, "分", null, null, "normal", false)); + response.getFundPool().add(metric("withdrawPending", "待审核提现", pendingWithdraw, "元", pendingWithdrawCount + " 笔", null, pendingWithdrawCount > 0 ? "danger" : "normal", false)); + } + + private void buildSnapshots(BossDashboardResponse response) { + DateTime today = DateUtil.date(); + DateRange morningRange = range(today, "00:00:00", "10:15:59"); + DateRange afternoonRange = range(today, "10:16:00", "14:55:59"); + + DailyMetrics morningMetrics = buildDailyMetrics(morningRange); + BossDashboardResponse.TodaySnapshot morning = snapshot("1015", "10:15 上午快报", morningRange.end, "上午抢购节点已完成,上一日寄卖商品消化情况请关注成交额、付款和采购用户。", morningMetrics, "success"); + response.getSnapshots().add(morning); + + String afternoonStatus = new Date().after(afternoonRange.end) ? "success" : "pending"; + String afternoonMessage = "success".equals(afternoonStatus) + ? "下午寄卖/转卖节点已完成,请关注用户抢购商品的再次上架与转卖承接。" + : "下午寄卖/转卖节点尚未生成,预计 14:55 后可查看用户抢购商品的再次上架情况。"; + DailyMetrics afternoonMetrics = "success".equals(afternoonStatus) ? buildDailyMetrics(afternoonRange) : new DailyMetrics(); + response.getSnapshots().add(snapshot("1455", "14:55 下午快报", afternoonRange.end, afternoonMessage, afternoonMetrics, afternoonStatus)); + } + + private void buildTrends(BossDashboardResponse response, DateTime businessDate) { + for (int i = 6; i >= 0; i--) { + DateTime date = DateUtil.offsetDay(businessDate, -i); + DailyMetrics metrics = buildDailyMetrics(dayRange(date)); + BossDashboardResponse.TrendPoint point = new BossDashboardResponse.TrendPoint(); + point.setDate(date.toString("MM-dd")); + point.setAmount(metrics.dealAmount); + point.setOrders(metrics.orderCount); + point.setNewUsers(metrics.newUsers); + point.setBonus(metrics.selfBonus.add(metrics.shareBonus)); + response.getTrends().add(point); + } + } + + private void buildRanks(BossDashboardResponse response) { + QueryWrapper userWrapper = new QueryWrapper() + .select("id", "nickname", "mobile", "self_bonus", "share_bonus", "coupon", "score") + .orderByDesc("IFNULL(self_bonus,0) + IFNULL(share_bonus,0) + IFNULL(coupon,0)") + .last("limit 3"); + List users = waUsersDao.selectList(userWrapper); + for (int i = 0; i < users.size(); i++) { + WaUsers user = users.get(i); + BigDecimal value = defaultDecimal(user.getSelfBonus()).add(defaultDecimal(user.getShareBonus())).add(defaultDecimal(user.getCoupon())); + response.getUserRanks().add(rank("u" + user.getId(), displayName(user), value, maskMobile(user.getMobile()), i == 0 ? "高价值" : null)); + } + + QueryWrapper teamWrapper = new QueryWrapper() + .select("pid as id", "COUNT(id) as memberCount", "IFNULL(SUM(self_bonus),0) as selfBonus", "IFNULL(SUM(share_bonus),0) as shareBonus") + .isNotNull("pid") + .gt("pid", 0) + .groupBy("pid") + .orderByDesc("IFNULL(SUM(self_bonus),0) + IFNULL(SUM(share_bonus),0)") + .last("limit 3"); + List> teams = waUsersDao.selectMaps(teamWrapper); + for (int i = 0; i < teams.size(); i++) { + Map team = teams.get(i); + BigDecimal selfBonus = decimal(team.get("selfBonus")); + BigDecimal shareBonus = decimal(team.get("shareBonus")); + String leaderId = stringValue(team.get("id")); + WaUsers leader = waUsersDao.selectById(leaderId); + String leaderName = leader == null ? "团队 " + leaderId : displayName(leader); + response.getTeamRanks().add(rank("t" + leaderId, leaderName, selfBonus.add(shareBonus), "成员 " + intValue(team.get("memberCount")) + " 人", i == 0 ? "TOP1" : null)); + } + + QueryWrapper productWrapper = new QueryWrapper() + .select("id", "title", "price", "created_at") + .eq("status", 1) + .orderByDesc("price") + .last("limit 3"); + List products = waMerchandiseDao.selectList(productWrapper); + for (int i = 0; i < products.size(); i++) { + WaMerchandise product = products.get(i); + response.getProductRanks().add(rank("p" + product.getId(), StrUtil.isBlank(product.getTitle()) ? "未命名商品" : product.getTitle(), defaultDecimal(product.getPrice()), "高货值待成交", i == 0 ? "滞销" : null)); + } + } + + private void buildRisks(BossDashboardResponse response) { + BigDecimal pendingWithdraw = sumWithdrawAmount(0); + if (pendingWithdraw.compareTo(BigDecimal.ZERO) > 0) { + response.getRisks().add(risk("r1", "red", "资金", "待审核提现", "当前待审核提现 " + pendingWithdraw + " 元,建议今日处理。")); + } + + Integer pendingOrders = countPendingOrders(); + if (pendingOrders > 0) { + response.getRisks().add(risk("r2", "yellow", "订单", "待支付订单未处理", "当前存在 " + pendingOrders + " 笔待支付订单,请关注付款转化。")); + } + + Integer hiddenProducts = countHiddenMerchandise(); + if (hiddenProducts > 0) { + response.getRisks().add(risk("r3", "gray", "商品", "隐藏寄售商品", "当前存在 " + hiddenProducts + " 个隐藏寄售商品,可按需核查。")); + } + } + + private BossDashboardResponse.KpiMetric metric(String key, String title, Object value, String unit, String trendLabel, BigDecimal trendValue, String status, Boolean featured) { + BossDashboardResponse.KpiMetric metric = new BossDashboardResponse.KpiMetric(); + metric.setKey(key); + metric.setTitle(title); + metric.setValue(value); + metric.setUnit(unit); + metric.setTrendLabel(trendLabel); + metric.setTrendValue(trendValue); + metric.setStatus(status); + metric.setFeatured(featured); + return metric; + } + + private BossDashboardResponse.TodaySnapshot snapshot(String slot, String title, Date generatedAt, String message, DailyMetrics metrics, String status) { + BossDashboardResponse.TodaySnapshot snapshot = new BossDashboardResponse.TodaySnapshot(); + snapshot.setSlot(slot); + snapshot.setTitle(title); + snapshot.setStatus(status); + snapshot.setGeneratedAt(DateUtil.formatDateTime(generatedAt)); + snapshot.setMessage(message); + snapshot.setPurchaseUsers(metrics.purchaseUsers); + snapshot.setOrderCount(metrics.orderCount); + snapshot.setDealAmount(metrics.dealAmount); + snapshot.setPaidAmount(metrics.dealAmount); + snapshot.setNewMerchandiseCount(metrics.newMerchandiseCount); + snapshot.setSelfBonusChange(metrics.selfBonus); + snapshot.setShareBonusChange(metrics.shareBonus); + return snapshot; + } + + private BossDashboardResponse.RankItem rank(String id, String name, BigDecimal value, String description, String badge) { + BossDashboardResponse.RankItem rank = new BossDashboardResponse.RankItem(); + rank.setId(id); + rank.setName(name); + rank.setValue(value); + rank.setDescription(description); + rank.setBadge(badge); + return rank; + } + + private BossDashboardResponse.RiskAlert risk(String id, String level, String type, String title, String description) { + BossDashboardResponse.RiskAlert risk = new BossDashboardResponse.RiskAlert(); + risk.setId(id); + risk.setLevel(level); + risk.setType(type); + risk.setTitle(title); + risk.setDescription(description); + risk.setDiscoveredAt(DateUtil.format(new Date(), "HH:mm")); + return risk; + } + + private void appendMetricsSection(StringBuilder html, String title, List metrics) { + html.append("

").append(escape(title)).append("

"); + for (BossDashboardResponse.KpiMetric metric : metrics) { + html.append("
").append(escape(metric.getTitle())).append(""); + html.append("").append(formatMetric(metric.getValue(), metric.getUnit())).append(""); + if (StrUtil.isNotBlank(metric.getTrendLabel()) || metric.getTrendValue() != null) { + html.append("").append(escape(metric.getTrendLabel())); + if (metric.getTrendValue() != null) { + html.append(" ").append(metric.getTrendValue()).append("%"); + } + html.append(""); + } + html.append("
"); + } + html.append("
"); + } + + private void appendTrendSection(StringBuilder html, BossDashboardResponse data) { + html.append("

最近 7 天趋势

"); + for (BossDashboardResponse.TrendPoint point : data.getTrends()) { + html.append("
").append(escape(point.getDate())).append(":").append(formatMoney(point.getAmount())).append(""); + html.append("").append(point.getOrders()).append(" 单 / 新增用户 ").append(point.getNewUsers()).append(" / 奖金 ").append(formatMoney(point.getBonus())).append("
"); + } + html.append("
"); + } + + private void appendRankSection(StringBuilder html, String title, List ranks) { + html.append("

").append(escape(title)).append("

"); + if (ranks.isEmpty()) { + html.append("
暂无数据当前实时数据未生成该排行。
"); + } + for (int i = 0; i < ranks.size(); i++) { + BossDashboardResponse.RankItem rank = ranks.get(i); + html.append("
").append(i + 1).append(". ").append(escape(rank.getName())).append(" · ").append(formatMoney(rank.getValue())).append(""); + html.append("").append(escape(rank.getDescription())); + if (StrUtil.isNotBlank(rank.getBadge())) { + html.append(" / ").append(escape(rank.getBadge())); + } + html.append("
"); + } + html.append("
"); + } + + private void appendRiskSection(StringBuilder html, BossDashboardResponse data) { + html.append("

风险预警

"); + if (data.getRisks().isEmpty()) { + html.append("
暂无风险当前实时数据未触发风险预警。
"); + } + for (BossDashboardResponse.RiskAlert risk : data.getRisks()) { + html.append("
"); + html.append(escape(risk.getType())).append(" / ").append(escape(risk.getTitle())).append(""); + html.append("

").append(escape(risk.getDescription())).append("

"); + html.append("发现时间:").append(escape(risk.getDiscoveredAt())).append("
"); + } + html.append("
"); + } + + private BigDecimal sumOrderAmount(Date start, Date end, Boolean paidOnly, Boolean isResell) { + QueryWrapper wrapper = new QueryWrapper().select("IFNULL(SUM(total_money),0) as total").between("buy_time", start, end); + wrapper.eq("is_cancel", 0); + if (Boolean.TRUE.equals(paidOnly)) { + wrapper.ge("status", 1); + } else if (Boolean.FALSE.equals(paidOnly)) { + wrapper.eq("status", 0); + } + if (isResell != null) { + wrapper.eq("is_resell", isResell ? 1 : 0); + } + return aggregateDecimal(waOrderDao.selectMaps(wrapper), "total"); + } + + private Integer countOrders(Date start, Date end, Boolean isResell) { + QueryWrapper wrapper = new QueryWrapper().between("buy_time", start, end).eq("is_cancel", 0); + if (isResell != null) { + wrapper.eq("is_resell", isResell ? 1 : 0); + } + return waOrderDao.selectCount(wrapper); + } + + private Integer distinctBuyerCount(Date start, Date end, Boolean isResell) { + QueryWrapper wrapper = new QueryWrapper().select("COUNT(DISTINCT buyer_id) as total").between("buy_time", start, end).eq("is_cancel", 0); + if (isResell != null) { + wrapper.eq("is_resell", isResell ? 1 : 0); + } + return aggregateInt(waOrderDao.selectMaps(wrapper), "total"); + } + + private Integer countUsers(Date start, Date end) { + return waUsersDao.selectCount(new QueryWrapper().between("join_time", start, end)); + } + + private Integer countMerchandise(Date start, Date end) { + return waMerchandiseDao.selectCount(new QueryWrapper().between("created_at", start, end)); + } + + private BigDecimal sumSelfBonus(Date start, Date end) { + QueryWrapper wrapper = new QueryWrapper().select("IFNULL(SUM(money),0) as total").eq("type", 1).between("created_at", start, end); + return aggregateDecimal(waSelfbonusLogDao.selectMaps(wrapper), "total"); + } + + private BigDecimal sumShareBonus(Date start, Date end) { + QueryWrapper wrapper = new QueryWrapper().select("IFNULL(SUM(money),0) as total").eq("type", 1).between("created_at", start, end); + return aggregateDecimal(waSharebonusLogDao.selectMaps(wrapper), "total"); + } + + private BigDecimal sumUsersDecimal(String column) { + QueryWrapper wrapper = new QueryWrapper().select("IFNULL(SUM(" + column + "),0) as total"); + return aggregateDecimal(waUsersDao.selectMaps(wrapper), "total"); + } + + private BigDecimal sumWithdrawAmount(Integer status) { + QueryWrapper wrapper = new QueryWrapper().select("IFNULL(SUM(money),0) as total").eq("status", status); + return aggregateDecimal(waWithdrawDao.selectMaps(wrapper), "total"); + } + + private Integer countWithdraw(Integer status) { + return waWithdrawDao.selectCount(new QueryWrapper().eq("status", status)); + } + + private Integer countPendingOrders() { + return waOrderDao.selectCount(new QueryWrapper().eq("status", 0).eq("is_cancel", 0)); + } + + private Integer countHiddenMerchandise() { + return waMerchandiseDao.selectCount(new QueryWrapper().eq("is_show", 0)); + } + + private BigDecimal ratio(BigDecimal current, BigDecimal previous) { + if (previous == null || previous.compareTo(BigDecimal.ZERO) == 0) { + return current != null && current.compareTo(BigDecimal.ZERO) > 0 ? BigDecimal.valueOf(100) : BigDecimal.ZERO; + } + return current.subtract(previous).multiply(BigDecimal.valueOf(100)).divide(previous, 1, RoundingMode.HALF_UP); + } + + private BigDecimal ratio(Integer current, Integer previous) { + return ratio(BigDecimal.valueOf(current == null ? 0 : current), BigDecimal.valueOf(previous == null ? 0 : previous)); + } + + private String statusByRatio(BigDecimal current, BigDecimal previous) { + BigDecimal value = ratio(current, previous); + return value.compareTo(BigDecimal.ZERO) >= 0 ? "success" : "warning"; + } + + private String statusByRatio(Integer current, Integer previous) { + return statusByRatio(BigDecimal.valueOf(current == null ? 0 : current), BigDecimal.valueOf(previous == null ? 0 : previous)); + } + + private String buildSummary(DailyMetrics metrics) { + if (metrics.dealAmount.compareTo(BigDecimal.ZERO) == 0 && metrics.orderCount == 0) { + return "当前日期暂无成交数据,请关注抢购与寄卖节点是否正常生成。"; + } + return "上个工作日成交 " + metrics.dealAmount + " 元,订单 " + metrics.orderCount + " 单,采购用户 " + metrics.purchaseUsers + " 人;请重点关注待支付、提现和寄售供给。"; + } + + private DateRange dayRange(DateTime date) { + return new DateRange(DateUtil.beginOfDay(date), DateUtil.endOfDay(date)); + } + + private DateTime previousWorkday(DateTime referenceDate) { + DateTime date = DateUtil.offsetDay(referenceDate, -1); + while (isWeekend(date)) { + date = DateUtil.offsetDay(date, -1); + } + return date; + } + + private boolean isWeekend(DateTime date) { + int dayOfWeek = DateUtil.dayOfWeek(date); + return dayOfWeek == Calendar.SATURDAY || dayOfWeek == Calendar.SUNDAY; + } + + private DateRange range(DateTime date, String startTime, String endTime) { + String day = date.toString("yyyy-MM-dd"); + return new DateRange(DateUtil.parse(day + " " + startTime), DateUtil.parse(day + " " + endTime)); + } + + private BigDecimal aggregateDecimal(List> maps, String key) { + if (maps == null || maps.isEmpty()) { + return BigDecimal.ZERO; + } + return decimal(maps.get(0).get(key)); + } + + private Integer aggregateInt(List> maps, String key) { + if (maps == null || maps.isEmpty()) { + return 0; + } + return intValue(maps.get(0).get(key)); + } + + private BigDecimal decimal(Object value) { + if (value == null) { + return BigDecimal.ZERO; + } + if (value instanceof BigDecimal) { + return (BigDecimal) value; + } + return new BigDecimal(String.valueOf(value)); + } + + private BigDecimal defaultDecimal(BigDecimal value) { + return value == null ? BigDecimal.ZERO : value; + } + + private Integer intValue(Object value) { + if (value == null) { + return 0; + } + if (value instanceof Number) { + return ((Number) value).intValue(); + } + return Integer.parseInt(String.valueOf(value)); + } + + private String stringValue(Object value) { + return value == null ? "" : String.valueOf(value); + } + + private String displayName(WaUsers user) { + if (StrUtil.isNotBlank(user.getNickname())) { + return user.getNickname(); + } + if (StrUtil.isNotBlank(user.getUsername())) { + return user.getUsername(); + } + return "用户 " + user.getId(); + } + + private String maskMobile(String mobile) { + if (StrUtil.isBlank(mobile) || mobile.length() < 7) { + return "手机号未完善"; + } + return mobile.substring(0, 3) + "****" + mobile.substring(mobile.length() - 4); + } + + private String formatMetric(Object value, String unit) { + if ("元".equals(unit)) { + return formatMoney(decimal(value)); + } + if (value == null) { + return "--"; + } + return escape(String.valueOf(value)) + (unit == null ? "" : escape(unit)); + } + + private String formatMoney(BigDecimal value) { + return "¥" + defaultDecimal(value).setScale(2, RoundingMode.HALF_UP).toPlainString(); + } + + private String escape(String value) { + if (value == null) { + return ""; + } + return value.replace("&", "&") + .replace("<", "<") + .replace(">", ">") + .replace("\"", """) + .replace("'", "'"); + } + + private static class DailyMetrics { + private BigDecimal dealAmount = BigDecimal.ZERO; + private Integer orderCount = 0; + private Integer purchaseUsers = 0; + private Integer newUsers = 0; + private Integer newMerchandiseCount = 0; + private BigDecimal selfBonus = BigDecimal.ZERO; + private BigDecimal shareBonus = BigDecimal.ZERO; + private BigDecimal pendingAmount = BigDecimal.ZERO; + } + + private static class DateRange { + private Date start; + private Date end; + + private DateRange(Date start, Date end) { + this.start = start; + this.end = end; + } + } +} diff --git a/dashboard-frontend/.env.development b/dashboard-frontend/.env.development new file mode 100644 index 0000000..965b808 --- /dev/null +++ b/dashboard-frontend/.env.development @@ -0,0 +1,5 @@ +VITE_APP_ENV=development +VITE_API_BASE_URL=/api/admin +VITE_MOCK_ENABLED=false +VITE_APP_TITLE=经营驾驶舱 +VITE_BUILD_VERSION=local diff --git a/dashboard-frontend/.gitignore b/dashboard-frontend/.gitignore new file mode 100644 index 0000000..a547bf3 --- /dev/null +++ b/dashboard-frontend/.gitignore @@ -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? diff --git a/dashboard-frontend/README.md b/dashboard-frontend/README.md new file mode 100644 index 0000000..82e6a8c --- /dev/null +++ b/dashboard-frontend/README.md @@ -0,0 +1,122 @@ +# Dashboard Frontend + +独立 H5 经营驾驶舱前端项目。第一阶段只使用本地 Mock 数据,不对接后端 API。 + +## 技术栈 + +- React 19 +- TypeScript +- Vite +- antd-mobile +- TanStack Query +- Zustand +- Axios +- ECharts +- MSW +- Vitest + +## 本地开发 + +```bash +nvm use --delete-prefix v24.14.1 +pnpm install +pnpm dev +``` + +默认访问: + +```text +http://localhost:5174/h5/dashboard/boss +``` + +## 第一阶段范围 + +- H5 移动端老板驾驶舱首页 +- 昨日经营核心 KPI +- 今日 10:15 / 14:55 节点快报 Mock +- 近 7 天交易趋势 +- 用户、团队、商品排行 +- 风险预警摘要 +- 底部 Tab 导航 +- MSW Mock 数据 + +## 校验 + +```bash +pnpm typecheck +pnpm test -- --run +pnpm build +``` +# 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 [Oxc](https://oxc.rs) +- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/) + +## React Compiler + +The React Compiler is not enabled on this template because of its impact on dev & build performances. To add it, see [this documentation](https://react.dev/learn/react-compiler/installation). + +## 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 defineConfig([ + globalIgnores(['dist']), + { + files: ['**/*.{ts,tsx}'], + extends: [ + // Other configs... + + // 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, + + // Other configs... + ], + languageOptions: { + parserOptions: { + project: ['./tsconfig.node.json', './tsconfig.app.json'], + tsconfigRootDir: import.meta.dirname, + }, + // other options... + }, + }, +]) +``` + +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 defineConfig([ + globalIgnores(['dist']), + { + files: ['**/*.{ts,tsx}'], + extends: [ + // Other configs... + // Enable lint rules for React + reactX.configs['recommended-typescript'], + // Enable lint rules for React DOM + reactDom.configs.recommended, + ], + languageOptions: { + parserOptions: { + project: ['./tsconfig.node.json', './tsconfig.app.json'], + tsconfigRootDir: import.meta.dirname, + }, + // other options... + }, + }, +]) +``` diff --git a/dashboard-frontend/eslint.config.js b/dashboard-frontend/eslint.config.js new file mode 100644 index 0000000..574d792 --- /dev/null +++ b/dashboard-frontend/eslint.config.js @@ -0,0 +1,22 @@ +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' +import { defineConfig, globalIgnores } from 'eslint/config' + +export default defineConfig([ + globalIgnores(['dist', 'public/mockServiceWorker.js']), + { + files: ['**/*.{ts,tsx}'], + extends: [ + js.configs.recommended, + tseslint.configs.recommended, + reactHooks.configs.flat.recommended, + reactRefresh.configs.vite, + ], + languageOptions: { + globals: globals.browser, + }, + }, +]) diff --git a/dashboard-frontend/index.html b/dashboard-frontend/index.html new file mode 100644 index 0000000..01f4e1b --- /dev/null +++ b/dashboard-frontend/index.html @@ -0,0 +1,13 @@ + + + + + + + dashboard-frontend + + +
+ + + diff --git a/dashboard-frontend/package.json b/dashboard-frontend/package.json new file mode 100644 index 0000000..1715dc2 --- /dev/null +++ b/dashboard-frontend/package.json @@ -0,0 +1,50 @@ +{ + "name": "dashboard-frontend", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc -b && vite build", + "lint": "eslint .", + "preview": "vite preview", + "typecheck": "tsc -b --noEmit", + "test": "vitest" + }, + "dependencies": { + "@tanstack/react-query": "^5.100.9", + "antd-mobile": "^5.42.3", + "antd-mobile-icons": "^0.3.0", + "axios": "^1.16.0", + "echarts": "^6.0.0", + "msw": "^2.14.5", + "react": "^19.2.5", + "react-dom": "^19.2.5", + "react-router-dom": "^7.15.0", + "zustand": "^5.0.13" + }, + "devDependencies": { + "@eslint/js": "^10.0.1", + "@testing-library/jest-dom": "^6.9.1", + "@testing-library/react": "^16.3.2", + "@testing-library/user-event": "^14.6.1", + "@types/node": "^24.12.2", + "@types/react": "^19.2.14", + "@types/react-dom": "^19.2.3", + "@vitejs/plugin-react": "^6.0.1", + "eslint": "^10.2.1", + "eslint-plugin-react-hooks": "^7.1.1", + "eslint-plugin-react-refresh": "^0.5.2", + "globals": "^17.5.0", + "jsdom": "^29.1.1", + "typescript": "~6.0.2", + "typescript-eslint": "^8.58.2", + "vite": "^8.0.10", + "vitest": "^4.1.5" + }, + "msw": { + "workerDirectory": [ + "public" + ] + } +} \ No newline at end of file diff --git a/dashboard-frontend/pnpm-lock.yaml b/dashboard-frontend/pnpm-lock.yaml new file mode 100644 index 0000000..c24247c --- /dev/null +++ b/dashboard-frontend/pnpm-lock.yaml @@ -0,0 +1,3461 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + '@tanstack/react-query': + specifier: ^5.100.9 + version: 5.100.9(react@19.2.6) + antd-mobile: + specifier: ^5.42.3 + version: 5.42.3(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + antd-mobile-icons: + specifier: ^0.3.0 + version: 0.3.0 + axios: + specifier: ^1.16.0 + version: 1.16.0 + echarts: + specifier: ^6.0.0 + version: 6.0.0 + msw: + specifier: ^2.14.5 + version: 2.14.5(@types/node@24.12.3)(typescript@6.0.3) + react: + specifier: ^19.2.5 + version: 19.2.6 + react-dom: + specifier: ^19.2.5 + version: 19.2.6(react@19.2.6) + react-router-dom: + specifier: ^7.15.0 + version: 7.15.0(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + zustand: + specifier: ^5.0.13 + version: 5.0.13(@types/react@19.2.14)(react@19.2.6)(use-sync-external-store@1.6.0(react@19.2.6)) + devDependencies: + '@eslint/js': + specifier: ^10.0.1 + version: 10.0.1(eslint@10.3.0) + '@testing-library/jest-dom': + specifier: ^6.9.1 + version: 6.9.1 + '@testing-library/react': + specifier: ^16.3.2 + version: 16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@testing-library/user-event': + specifier: ^14.6.1 + version: 14.6.1(@testing-library/dom@10.4.1) + '@types/node': + specifier: ^24.12.2 + version: 24.12.3 + '@types/react': + specifier: ^19.2.14 + version: 19.2.14 + '@types/react-dom': + specifier: ^19.2.3 + version: 19.2.3(@types/react@19.2.14) + '@vitejs/plugin-react': + specifier: ^6.0.1 + version: 6.0.1(vite@8.0.11(@types/node@24.12.3)) + eslint: + specifier: ^10.2.1 + version: 10.3.0 + eslint-plugin-react-hooks: + specifier: ^7.1.1 + version: 7.1.1(eslint@10.3.0) + eslint-plugin-react-refresh: + specifier: ^0.5.2 + version: 0.5.2(eslint@10.3.0) + globals: + specifier: ^17.5.0 + version: 17.6.0 + jsdom: + specifier: ^29.1.1 + version: 29.1.1 + typescript: + specifier: ~6.0.2 + version: 6.0.3 + typescript-eslint: + specifier: ^8.58.2 + version: 8.59.2(eslint@10.3.0)(typescript@6.0.3) + vite: + specifier: ^8.0.10 + version: 8.0.11(@types/node@24.12.3) + vitest: + specifier: ^4.1.5 + version: 4.1.5(@types/node@24.12.3)(jsdom@29.1.1)(msw@2.14.5(@types/node@24.12.3)(typescript@6.0.3))(vite@8.0.11(@types/node@24.12.3)) + +packages: + + '@adobe/css-tools@4.4.4': + resolution: {integrity: sha512-Elp+iwUx5rN5+Y8xLt5/GRoG20WGoDCQ/1Fb+1LiGtvwbDavuSk0jhD/eZdckHAuzcDzccnkv+rEjyWfRx18gg==} + + '@asamuzakjp/css-color@5.1.11': + resolution: {integrity: sha512-KVw6qIiCTUQhByfTd78h2yD1/00waTmm9uy/R7Ck/ctUyAPj+AEDLkQIdJW0T8+qGgj3j5bpNKK7Q3G+LedJWg==} + engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + + '@asamuzakjp/dom-selector@7.1.1': + resolution: {integrity: sha512-67RZDnYRc8H/8MLDgQCDE//zoqVFwajkepHZgmXrbwybzXOEwOWGPYGmALYl9J2DOLfFPPs6kKCqmbzV895hTQ==} + engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + + '@asamuzakjp/generational-cache@1.0.1': + resolution: {integrity: sha512-wajfB8KqzMCN2KGNFdLkReeHncd0AslUSrvHVvvYWuU8ghncRJoA50kT3zP9MVL0+9g4/67H+cdvBskj9THPzg==} + engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + + '@asamuzakjp/nwsapi@2.3.9': + resolution: {integrity: sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==} + + '@babel/code-frame@7.29.0': + resolution: {integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==} + engines: {node: '>=6.9.0'} + + '@babel/compat-data@7.29.3': + resolution: {integrity: sha512-LIVqM46zQWZhj17qA8wb4nW/ixr2y1Nw+r1etiAWgRM6U1IqP+LNhL1yg440jYZR72jCWcWbLWzIosH+uP1fqg==} + engines: {node: '>=6.9.0'} + + '@babel/core@7.29.0': + resolution: {integrity: sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==} + engines: {node: '>=6.9.0'} + + '@babel/generator@7.29.1': + resolution: {integrity: sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-compilation-targets@7.28.6': + resolution: {integrity: sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-globals@7.28.0': + resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-imports@7.28.6': + resolution: {integrity: sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-transforms@7.28.6': + resolution: {integrity: sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-string-parser@7.27.1': + resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.28.5': + resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-option@7.27.1': + resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} + engines: {node: '>=6.9.0'} + + '@babel/helpers@7.29.2': + resolution: {integrity: sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.29.3': + resolution: {integrity: sha512-b3ctpQwp+PROvU/cttc4OYl4MzfJUWy6FZg+PMXfzmt/+39iHVF0sDfqay8TQM3JA2EUOyKcFZt75jWriQijsA==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/runtime@7.29.2': + resolution: {integrity: sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==} + engines: {node: '>=6.9.0'} + + '@babel/template@7.28.6': + resolution: {integrity: sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==} + engines: {node: '>=6.9.0'} + + '@babel/traverse@7.29.0': + resolution: {integrity: sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==} + engines: {node: '>=6.9.0'} + + '@babel/types@7.29.0': + resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==} + engines: {node: '>=6.9.0'} + + '@bramus/specificity@2.4.2': + resolution: {integrity: sha512-ctxtJ/eA+t+6q2++vj5j7FYX3nRu311q1wfYH3xjlLOsczhlhxAg2FWNUXhpGvAw3BWo1xBcvOV6/YLc2r5FJw==} + hasBin: true + + '@csstools/color-helpers@6.0.2': + resolution: {integrity: sha512-LMGQLS9EuADloEFkcTBR3BwV/CGHV7zyDxVRtVDTwdI2Ca4it0CCVTT9wCkxSgokjE5Ho41hEPgb8OEUwoXr6Q==} + engines: {node: '>=20.19.0'} + + '@csstools/css-calc@3.2.0': + resolution: {integrity: sha512-bR9e6o2BDB12jzN/gIbjHa5wLJ4UjD1CB9pM7ehlc0ddk6EBz+yYS1EV2MF55/HUxrHcB/hehAyt5vhsA3hx7w==} + engines: {node: '>=20.19.0'} + peerDependencies: + '@csstools/css-parser-algorithms': ^4.0.0 + '@csstools/css-tokenizer': ^4.0.0 + + '@csstools/css-color-parser@4.1.0': + resolution: {integrity: sha512-U0KhLYmy2GVj6q4T3WaAe6NPuFYCPQoE3b0dRGxejWDgcPp8TP7S5rVdM5ZrFaqu4N67X8YaPBw14dQSYx3IyQ==} + engines: {node: '>=20.19.0'} + peerDependencies: + '@csstools/css-parser-algorithms': ^4.0.0 + '@csstools/css-tokenizer': ^4.0.0 + + '@csstools/css-parser-algorithms@4.0.0': + resolution: {integrity: sha512-+B87qS7fIG3L5h3qwJ/IFbjoVoOe/bpOdh9hAjXbvx0o8ImEmUsGXN0inFOnk2ChCFgqkkGFQ+TpM5rbhkKe4w==} + engines: {node: '>=20.19.0'} + peerDependencies: + '@csstools/css-tokenizer': ^4.0.0 + + '@csstools/css-syntax-patches-for-csstree@1.1.3': + resolution: {integrity: sha512-SH60bMfrRCJF3morcdk57WklujF4Jr/EsQUzqkarfHXEFcAR1gg7fS/chAE922Sehgzc1/+Tz5H3Ypa1HiEKrg==} + peerDependencies: + css-tree: ^3.2.1 + peerDependenciesMeta: + css-tree: + optional: true + + '@csstools/css-tokenizer@4.0.0': + resolution: {integrity: sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA==} + engines: {node: '>=20.19.0'} + + '@emnapi/core@1.10.0': + resolution: {integrity: sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==} + + '@emnapi/runtime@1.10.0': + resolution: {integrity: sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==} + + '@emnapi/wasi-threads@1.2.1': + resolution: {integrity: sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==} + + '@eslint-community/eslint-utils@4.9.1': + resolution: {integrity: sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + + '@eslint-community/regexpp@4.12.2': + resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + + '@eslint/config-array@0.23.5': + resolution: {integrity: sha512-Y3kKLvC1dvTOT+oGlqNQ1XLqK6D1HU2YXPc52NmAlJZbMMWDzGYXMiPRJ8TYD39muD/OTjlZmNJ4ib7dvSrMBA==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + '@eslint/config-helpers@0.5.5': + resolution: {integrity: sha512-eIJYKTCECbP/nsKaaruF6LW967mtbQbsw4JTtSVkUQc9MneSkbrgPJAbKl9nWr0ZeowV8BfsarBmPpBzGelA2w==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + '@eslint/core@1.2.1': + resolution: {integrity: sha512-MwcE1P+AZ4C6DWlpin/OmOA54mmIZ/+xZuJiQd4SyB29oAJjN30UW9wkKNptW2ctp4cEsvhlLY/CsQ1uoHDloQ==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + '@eslint/js@10.0.1': + resolution: {integrity: sha512-zeR9k5pd4gxjZ0abRoIaxdc7I3nDktoXZk2qOv9gCNWx3mVwEn32VRhyLaRsDiJjTs0xq/T8mfPtyuXu7GWBcA==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + peerDependencies: + eslint: ^10.0.0 + peerDependenciesMeta: + eslint: + optional: true + + '@eslint/object-schema@3.0.5': + resolution: {integrity: sha512-vqTaUEgxzm+YDSdElad6PiRoX4t8VGDjCtt05zn4nU810UIx/uNEV7/lZJ6KwFThKZOzOxzXy48da+No7HZaMw==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + '@eslint/plugin-kit@0.7.1': + resolution: {integrity: sha512-rZAP3aVgB9ds9KOeUSL+zZ21hPmo8dh6fnIFwRQj5EAZl9gzR7wxYbYXYysAM8CTqGmUGyp2S4kUdV17MnGuWQ==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + '@exodus/bytes@1.15.0': + resolution: {integrity: sha512-UY0nlA+feH81UGSHv92sLEPLCeZFjXOuHhrIo0HQydScuQc8s0A7kL/UdgwgDq8g8ilksmuoF35YVTNphV2aBQ==} + engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + peerDependencies: + '@noble/hashes': ^1.8.0 || ^2.0.0 + peerDependenciesMeta: + '@noble/hashes': + optional: true + + '@floating-ui/core@1.7.5': + resolution: {integrity: sha512-1Ih4WTWyw0+lKyFMcBHGbb5U5FtuHJuujoyyr5zTaWS5EYMeT6Jb2AuDeftsCsEuchO+mM2ij5+q9crhydzLhQ==} + + '@floating-ui/dom@1.7.6': + resolution: {integrity: sha512-9gZSAI5XM36880PPMm//9dfiEngYoC6Am2izES1FF406YFsjvyBMmeJ2g4SAju3xWwtuynNRFL2s9hgxpLI5SQ==} + + '@floating-ui/utils@0.2.11': + resolution: {integrity: sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg==} + + '@humanfs/core@0.19.2': + resolution: {integrity: sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==} + engines: {node: '>=18.18.0'} + + '@humanfs/node@0.16.8': + resolution: {integrity: sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==} + engines: {node: '>=18.18.0'} + + '@humanfs/types@0.15.0': + resolution: {integrity: sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==} + engines: {node: '>=18.18.0'} + + '@humanwhocodes/module-importer@1.0.1': + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} + + '@humanwhocodes/retry@0.4.3': + resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} + engines: {node: '>=18.18'} + + '@inquirer/ansi@2.0.5': + resolution: {integrity: sha512-doc2sWgJpbFQ64UflSVd17ibMGDuxO1yKgOgLMwavzESnXjFWJqUeG8saYosqKpHp4kWiM5x1nXvEjbpx90gzw==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} + + '@inquirer/confirm@6.0.13': + resolution: {integrity: sha512-wkGPC7yJ5WJk1DJ5SX7fzk+gfj4BM8cf5dDDi71B/551xHrdsZVRJOC0WyikXd0pEsb/9cLniuE4atbsMqmFkw==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/core@11.1.10': + resolution: {integrity: sha512-a4Q5BXHQAHa9eO202sTaFCHFYVB3x5fauDuThEAdZ9gfn76pSxiKU7wWcEH0N1O0XmQvNfQNU6QXpiRxmYQx+A==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/figures@2.0.5': + resolution: {integrity: sha512-NsSs4kzfm12lNetHwAn3GEuH317IzpwrMCbOuMIVytpjnJ90YYHNwdRgYGuKmVxwuIqSgqk3M5qqQt1cDk0tGQ==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} + + '@inquirer/type@4.0.5': + resolution: {integrity: sha512-aetVUNeKNc/VriqXlw1NRSW0zhMBB0W4bNbWRJgzRl/3d0QNDQFfk0GO5SDdtjMZVg6o8ZKEiadd7SCCzoOn5Q==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@jridgewell/gen-mapping@0.3.13': + resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} + + '@jridgewell/remapping@2.3.5': + resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + + '@jridgewell/trace-mapping@0.3.31': + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + + '@mswjs/interceptors@0.41.8': + resolution: {integrity: sha512-pRLMNKTSGRoLq+KnEB/7OY5vijw1XmcheAAOiv6pj7W1FG32kAGqj1C/RK/cqxRGr1Fh+zBi8sDur8kj3EQv6A==} + engines: {node: '>=18'} + + '@napi-rs/wasm-runtime@1.1.4': + resolution: {integrity: sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow==} + peerDependencies: + '@emnapi/core': ^1.7.1 + '@emnapi/runtime': ^1.7.1 + + '@open-draft/deferred-promise@2.2.0': + resolution: {integrity: sha512-CecwLWx3rhxVQF6V4bAgPS5t+So2sTbPgAzafKkVizyi7tlwpcFpdFqq+wqF2OwNBmqFuu6tOyouTuxgpMfzmA==} + + '@open-draft/deferred-promise@3.0.0': + resolution: {integrity: sha512-XW375UK8/9SqUVNVa6M0yEy8+iTi4QN5VZ7aZuRFQmy76LRwI9wy5F4YIBU6T+eTe2/DNDo8tqu8RHlwLHM6RA==} + + '@open-draft/logger@0.3.0': + resolution: {integrity: sha512-X2g45fzhxH238HKO4xbSr7+wBS8Fvw6ixhTDuvLd5mqh6bJJCFAPwU9mPDxbcrRtfxv4u5IHCEH77BmxvXmmxQ==} + + '@open-draft/until@2.1.0': + resolution: {integrity: sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg==} + + '@oxc-project/types@0.128.0': + resolution: {integrity: sha512-huv1Y/LzBJkBVHt3OlC7u0zHBW9qXf1FdD7sGmc1rXc2P1mTwHssYv7jyGx5KAACSCH+9B3Bhn6Z9luHRvf7pQ==} + + '@rc-component/mini-decimal@1.1.3': + resolution: {integrity: sha512-bk/FJ09fLf+NLODMAFll6CfYrHPBioTedhW6lxDBuuWucJEqFUd4l/D/5JgIi3dina6sYahB8iuPAZTNz2pMxw==} + engines: {node: '>=8.x'} + + '@react-spring/animated@9.6.1': + resolution: {integrity: sha512-ls/rJBrAqiAYozjLo5EPPLLOb1LM0lNVQcXODTC1SMtS6DbuBCPaKco5svFUQFMP2dso3O+qcC4k9FsKc0KxMQ==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + + '@react-spring/core@9.6.1': + resolution: {integrity: sha512-3HAAinAyCPessyQNNXe5W0OHzRfa8Yo5P748paPcmMowZ/4sMfaZ2ZB6e5x5khQI8NusOHj8nquoutd6FRY5WQ==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + + '@react-spring/rafz@9.6.1': + resolution: {integrity: sha512-v6qbgNRpztJFFfSE3e2W1Uz+g8KnIBs6SmzCzcVVF61GdGfGOuBrbjIcp+nUz301awVmREKi4eMQb2Ab2gGgyQ==} + + '@react-spring/shared@9.6.1': + resolution: {integrity: sha512-PBFBXabxFEuF8enNLkVqMC9h5uLRBo6GQhRMQT/nRTnemVENimgRd+0ZT4yFnAQ0AxWNiJfX3qux+bW2LbG6Bw==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + + '@react-spring/types@9.6.1': + resolution: {integrity: sha512-POu8Mk0hIU3lRXB3bGIGe4VHIwwDsQyoD1F394OK7STTiX9w4dG3cTLljjYswkQN+hDSHRrj4O36kuVa7KPU8Q==} + + '@react-spring/web@9.6.1': + resolution: {integrity: sha512-X2zR6q2Z+FjsWfGAmAXlQaoUHbPmfuCaXpuM6TcwXPpLE1ZD4A1eys/wpXboFQmDkjnrlTmKvpVna1MjWpZ5Hw==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + + '@rolldown/binding-android-arm64@1.0.0-rc.18': + resolution: {integrity: sha512-lIDyUAfD7U3+BWKzdxMbJcsYHuqXqmGz40aeRqvuAm3y5TkJSYTBW2RDrn65DJFPQqVjUAUqq5uz8urzQ8aBdQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + + '@rolldown/binding-darwin-arm64@1.0.0-rc.18': + resolution: {integrity: sha512-apJq2ktnGp27nSInMR5Vcj8kY6xJzDAvfdIFlpDcAK/w4cDO58qVoi1YQsES/SKiFNge/6e4CUzgjfHduYqWpQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + + '@rolldown/binding-darwin-x64@1.0.0-rc.18': + resolution: {integrity: sha512-5Ofot8xbs+pxRHJqm9/9N/4sTQOvdrwEsmPE9pdLEEoAbdZtG6F2LMDfO1sp6ZAtXJuJV/21ew2srq3W8NXB5g==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + + '@rolldown/binding-freebsd-x64@1.0.0-rc.18': + resolution: {integrity: sha512-7h8eeOTT1eyqJyx64BFCnWZpNm486hGWt2sqeLLgDxA0xI1oGZ9H7gK1S85uNGmBhkdPwa/6reTxfFFKvIsebw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + + '@rolldown/binding-linux-arm-gnueabihf@1.0.0-rc.18': + resolution: {integrity: sha512-eRcm/HVt9U/JFu5RKAEKwGQYtDCKWLiaH6wOnsSEp6NMBb/3Os8LgHZlNyzMpFVNmiiMFlfb2zEnebfzJrHFmg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@rolldown/binding-linux-arm64-gnu@1.0.0-rc.18': + resolution: {integrity: sha512-SOrT/cT4ukTmgnrEz/Hg3m7LBnuCLW9psDeMKrimRWY4I8DmnO7Lco8W2vtqPmMkbVu8iJ+g4GFLVLLOVjJ9DQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-arm64-musl@1.0.0-rc.18': + resolution: {integrity: sha512-QWjdxN1HJCpBTAcZ5N5F7wju3gVPzRzSpmGzx7na0c/1qpN9CFil+xt+l9lV/1M6/gqHSNXCiqPfwhVJPeLnug==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@rolldown/binding-linux-ppc64-gnu@1.0.0-rc.18': + resolution: {integrity: sha512-ugCOyj7a4d9h3q9B+wXmf6g3a68UsjGh6dob5DHevHGMwDUbhsYNbSPxJsENcIttJZ9jv7qGM2UesLw5jqIhdg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-s390x-gnu@1.0.0-rc.18': + resolution: {integrity: sha512-kKWRhbsotpXkGbcd5dllUWg5gEXcDAa8u5YnP9AV5DYNbvJHGzzuwv7dpmhc8NqKMJldl0a+x76IHbspEpEmdA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-x64-gnu@1.0.0-rc.18': + resolution: {integrity: sha512-uCo8ElcCIAMyYAZyuIZ81oFkhTSIllNvUCHCAlbhlN4ji3uC28h7IIdlXyIvGO7HsuqnV9p3rD/bpH7XhIyhRw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-x64-musl@1.0.0-rc.18': + resolution: {integrity: sha512-XNOQZtuE6yUIvx4rwGemwh8kpL1xvU41FXy/s9K7T/3JVcqGzo3NfKM2HrbrGgfPYGFW42f07Wk++aOC6B9NWA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [musl] + + '@rolldown/binding-openharmony-arm64@1.0.0-rc.18': + resolution: {integrity: sha512-tSn/kzrfa7tNOXr7sEacDBN4YsIqTyLqh45IO0nHDwtpKIDNDJr+VFojt+4klSpChxB29JLyduSsE0MKEwa65A==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + + '@rolldown/binding-wasm32-wasi@1.0.0-rc.18': + resolution: {integrity: sha512-+J9YGmc+czgqlhYmwun3S3O0FIZhsH8ep2456xwjAdIOmuJxM7xz4P4PtrxU+Bz17a/5bqPA8o3HAAoX0teUdg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [wasm32] + + '@rolldown/binding-win32-arm64-msvc@1.0.0-rc.18': + resolution: {integrity: sha512-zsu47DgU0FQzSwi6sU9dZoEdUv7pc1AptSEz/Z8HBg54sV0Pbs3N0+CrIbTsgiu6EyoaNN9CHboqbLaz9lhOyQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + + '@rolldown/binding-win32-x64-msvc@1.0.0-rc.18': + resolution: {integrity: sha512-7H+3yqGgmnlDTRRhw/xpYY9J1kf4GC681nVc4GqKhExZTDrVVrV2tsOR9kso0fvgBdcTCcQShx4SLLoHgaLwhg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + + '@rolldown/pluginutils@1.0.0-rc.18': + resolution: {integrity: sha512-CUY5Mnhe64xQBGZEEXQ5WyZwsc1JU3vAZLIxtrsBt3LO6UOb+C8GunVKqe9sT8NeWb4lqSaoJtp2xo6GxT1MNw==} + + '@rolldown/pluginutils@1.0.0-rc.7': + resolution: {integrity: sha512-qujRfC8sFVInYSPPMLQByRh7zhwkGFS4+tyMQ83srV1qrxL4g8E2tyxVVyxd0+8QeBM1mIk9KbWxkegRr76XzA==} + + '@standard-schema/spec@1.1.0': + resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} + + '@tanstack/query-core@5.100.9': + resolution: {integrity: sha512-SJSFw1S8+kQ0+knv/XGfrbocWoAlT7vDKsSImtLx3ZPQmEcR46hkDjLSvynSy25N8Ms4tIEini1FuBd5k7IscQ==} + + '@tanstack/react-query@5.100.9': + resolution: {integrity: sha512-Oa44XkaI3kCNN6ME0KByU3xT3SEUNOMfZpHxL6+wFoTm+OeUFYHKdeYVe0aOXlRDm/f15sgLwEt2HDorIdW8+A==} + peerDependencies: + react: ^18 || ^19 + + '@testing-library/dom@10.4.1': + resolution: {integrity: sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==} + engines: {node: '>=18'} + + '@testing-library/jest-dom@6.9.1': + resolution: {integrity: sha512-zIcONa+hVtVSSep9UT3jZ5rizo2BsxgyDYU7WFD5eICBE7no3881HGeb/QkGfsJs6JTkY1aQhT7rIPC7e+0nnA==} + engines: {node: '>=14', npm: '>=6', yarn: '>=1'} + + '@testing-library/react@16.3.2': + resolution: {integrity: sha512-XU5/SytQM+ykqMnAnvB2umaJNIOsLF3PVv//1Ew4CTcpz0/BRyy/af40qqrt7SjKpDdT1saBMc42CUok5gaw+g==} + engines: {node: '>=18'} + peerDependencies: + '@testing-library/dom': ^10.0.0 + '@types/react': ^18.0.0 || ^19.0.0 + '@types/react-dom': ^18.0.0 || ^19.0.0 + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@testing-library/user-event@14.6.1': + resolution: {integrity: sha512-vq7fv0rnt+QTXgPxr5Hjc210p6YKq2kmdziLgnsZGgLJ9e6VAShx1pACLuRjd/AS/sr7phAR58OIIpf0LlmQNw==} + engines: {node: '>=12', npm: '>=6'} + peerDependencies: + '@testing-library/dom': '>=7.21.4' + + '@tybys/wasm-util@0.10.2': + resolution: {integrity: sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==} + + '@types/aria-query@5.0.4': + resolution: {integrity: sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==} + + '@types/chai@5.2.3': + resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} + + '@types/deep-eql@4.0.2': + resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} + + '@types/esrecurse@4.3.1': + resolution: {integrity: sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==} + + '@types/estree@1.0.9': + resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==} + + '@types/js-cookie@3.0.6': + resolution: {integrity: sha512-wkw9yd1kEXOPnvEeEV1Go1MmxtBJL0RR79aOTAApecWFVu7w0NNXNqhcWgvw2YgZDYadliXkl14pa3WXw5jlCQ==} + + '@types/json-schema@7.0.15': + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + + '@types/node@24.12.3': + resolution: {integrity: sha512-8oljBDGun9cIsZRJR6fkihn0TSXJI0UDOOhncYaERq6M0JMDoPLxyscwruJcb4GKS6dvK/d8xebYBg27h/duaQ==} + + '@types/react-dom@19.2.3': + resolution: {integrity: sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==} + peerDependencies: + '@types/react': ^19.2.0 + + '@types/react@19.2.14': + resolution: {integrity: sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==} + + '@types/set-cookie-parser@2.4.10': + resolution: {integrity: sha512-GGmQVGpQWUe5qglJozEjZV/5dyxbOOZ0LHe/lqyWssB88Y4svNfst0uqBVscdDeIKl5Jy5+aPSvy7mI9tYRguw==} + + '@types/statuses@2.0.6': + resolution: {integrity: sha512-xMAgYwceFhRA2zY+XbEA7mxYbA093wdiW8Vu6gZPGWy9cmOyU9XesH1tNcEWsKFd5Vzrqx5T3D38PWx1FIIXkA==} + + '@typescript-eslint/eslint-plugin@8.59.2': + resolution: {integrity: sha512-j/bwmkBvHUtPNxzuWe5z6BEk3q54YRyGlBXkSsmfoih7zNrBvl5A9A98anlp/7JbyZcWIJ8KXo/3Tq/DjFLtuQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + '@typescript-eslint/parser': ^8.59.2 + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/parser@8.59.2': + resolution: {integrity: sha512-plR3pp6D+SSUn1HM7xvSkx12/DhoHInI2YF35KAcVFNZvlC0gtrWqx7Qq1oH2Ssgi0vlFRCTbP+DZc7B9+TtsQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/project-service@8.59.2': + resolution: {integrity: sha512-+2hqvEkeyf/0FBor67duF0Ll7Ot8jyKzDQOSrxazF/danillRq2DwR9dLptsXpoZQqxE1UisSmoZewrlPas9Vw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/scope-manager@8.59.2': + resolution: {integrity: sha512-JzfyEpEtOU89CcFSwyNS3mu4MLvLSXqnmX05+aKBDM+TdR5jzcGOEBwxwGNxrEQ7p/z6kK2WyioCGBf2zZBnvg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/tsconfig-utils@8.59.2': + resolution: {integrity: sha512-BKK4alN7oi4C/zv4VqHQ+uRU+lTa6JGIZ7s1juw7b3RHo9OfKB+bKX3u0iVZetdsUCBBkSbdWbarJbmN0fTeSw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/type-utils@8.59.2': + resolution: {integrity: sha512-nhqaj1nmTdVVl/BP5omXNRGO38jn5iosis2vbdmupF2txCf8ylWT8lx+JlvMYYVqzGVKtjojUFoQ3JRWK+mfzQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/types@8.59.2': + resolution: {integrity: sha512-e82GVOE8Ps3E++Egvb6Y3Dw0S10u8NkQ9KXmtRhCWJJ8kDhOJTvtMAWnFL16kB1583goCWXsr0NieKCZMs2/0Q==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/typescript-estree@8.59.2': + resolution: {integrity: sha512-o0XPGNwcWw+FIwStOWn+BwBuEmL6QXP0rsvAFg7ET1dey1Nr6Wb1ac8p5HEsK0ygO/6mUxlk+YWQD9xcb/nnXg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/utils@8.59.2': + resolution: {integrity: sha512-Juw3EinkXqjaffxz6roowvV7GZT/kET5vSKKZT6upl5TXdWkLkYmNPXwDDL2Vkt2DPn0nODIS4egC/0AGxKo/Q==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/visitor-keys@8.59.2': + resolution: {integrity: sha512-NwjLUnGy8/Zfx23fl50tRC8rYaYnM52xNRYFAXvmiil9yh1+K6aRVQMnzW6gQB/1DLgWt977lYQn7C+wtgXZiA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@use-gesture/core@10.3.0': + resolution: {integrity: sha512-rh+6MND31zfHcy9VU3dOZCqGY511lvGcfyJenN4cWZe0u1BH6brBpBddLVXhF2r4BMqWbvxfsbL7D287thJU2A==} + + '@use-gesture/react@10.3.0': + resolution: {integrity: sha512-3zc+Ve99z4usVP6l9knYVbVnZgfqhKah7sIG+PS2w+vpig2v2OLct05vs+ZXMzwxdNCMka8B+8WlOo0z6Pn6DA==} + peerDependencies: + react: '>= 16.8.0' + + '@vitejs/plugin-react@6.0.1': + resolution: {integrity: sha512-l9X/E3cDb+xY3SWzlG1MOGt2usfEHGMNIaegaUGFsLkb3RCn/k8/TOXBcab+OndDI4TBtktT8/9BwwW8Vi9KUQ==} + engines: {node: ^20.19.0 || >=22.12.0} + peerDependencies: + '@rolldown/plugin-babel': ^0.1.7 || ^0.2.0 + babel-plugin-react-compiler: ^1.0.0 + vite: ^8.0.0 + peerDependenciesMeta: + '@rolldown/plugin-babel': + optional: true + babel-plugin-react-compiler: + optional: true + + '@vitest/expect@4.1.5': + resolution: {integrity: sha512-PWBaRY5JoKuRnHlUHfpV/KohFylaDZTupcXN1H9vYryNLOnitSw60Mw9IAE2r67NbwwzBw/Cc/8q9BK3kIX8Kw==} + + '@vitest/mocker@4.1.5': + resolution: {integrity: sha512-/x2EmFC4mT4NNzqvC3fmesuV97w5FC903KPmey4gsnJiMQ3Be1IlDKVaDaG8iqaLFHqJ2FVEkxZk5VmeLjIItw==} + peerDependencies: + msw: ^2.4.9 + vite: ^6.0.0 || ^7.0.0 || ^8.0.0 + peerDependenciesMeta: + msw: + optional: true + vite: + optional: true + + '@vitest/pretty-format@4.1.5': + resolution: {integrity: sha512-7I3q6l5qr03dVfMX2wCo9FxwSJbPdwKjy2uu/YPpU3wfHvIL4QHwVRp57OfGrDFeUJ8/8QdfBKIV12FTtLn00g==} + + '@vitest/runner@4.1.5': + resolution: {integrity: sha512-2D+o7Pr82IEO46YPpoA/YU0neeyr6FTerQb5Ro7BUnBuv6NQtT/kmVnczngiMEBhzgqz2UZYl5gArejsyERDSQ==} + + '@vitest/snapshot@4.1.5': + resolution: {integrity: sha512-zypXEt4KH/XgKGPUz4eC2AvErYx0My5hfL8oDb1HzGFpEk1P62bxSohdyOmvz+d9UJwanI68MKwr2EquOaOgMQ==} + + '@vitest/spy@4.1.5': + resolution: {integrity: sha512-2lNOsh6+R2Idnf1TCZqSwYlKN2E/iDlD8sgU59kYVl+OMDmvldO1VDk39smRfpUNwYpNRVn3w4YfuC7KfbBnkQ==} + + '@vitest/utils@4.1.5': + resolution: {integrity: sha512-76wdkrmfXfqGjueGgnb45ITPyUi1ycZ4IHgC2bhPDUfWHklY/q3MdLOAB+TF1e6xfl8NxNY0ZYaPCFNWSsw3Ug==} + + acorn-jsx@5.3.2: + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + + acorn@8.16.0: + resolution: {integrity: sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==} + engines: {node: '>=0.4.0'} + hasBin: true + + ahooks@3.9.7: + resolution: {integrity: sha512-S0lvzhbdlhK36RFBkGv+RbOM/dbbweym+BIHM/bwwuWVSVN5TuVErHPMWo4w0t1NDYg5KPp2iEf7Y7E5LASYiw==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + ajv@6.15.0: + resolution: {integrity: sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==} + + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + ansi-styles@5.2.0: + resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} + engines: {node: '>=10'} + + antd-mobile-icons@0.3.0: + resolution: {integrity: sha512-rqINQpJWZWrva9moCd1Ye695MZYWmqLPE+bY8d2xLRy7iSQwPsinCdZYjpUPp2zL/LnKYSyXxP2ut2A+DC+whQ==} + + antd-mobile-v5-count@1.0.1: + resolution: {integrity: sha512-YGsiEDCPUDz3SzfXi6gLZn/HpeSMW+jgPc4qiYUr1fSopg3hkUie2TnooJdExgfiETHefH3Ggs58He0OVfegLA==} + + antd-mobile@5.42.3: + resolution: {integrity: sha512-HLykA0Cr2am7dgEch/TRUnr4GHgTOM59rA9CVjgBJVVJ8dk2w8uUBz//Bji19sRH2leiUObWY7KQZpsWF+WQeg==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + aria-query@5.3.0: + resolution: {integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==} + + aria-query@5.3.2: + resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==} + engines: {node: '>= 0.4'} + + assertion-error@2.0.1: + resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} + engines: {node: '>=12'} + + async-validator@4.2.5: + resolution: {integrity: sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg==} + + asynckit@0.4.0: + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + + axios@1.16.0: + resolution: {integrity: sha512-6hp5CwvTPlN2A31g5dxnwAX0orzM7pmCRDLnZSX772mv8WDqICwFjowHuPs04Mc8deIld1+ejhtaMn5vp6b+1w==} + + balanced-match@4.0.4: + resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} + engines: {node: 18 || 20 || >=22} + + baseline-browser-mapping@2.10.29: + resolution: {integrity: sha512-Asa2krT+XTPZINCS+2QcyS8WTkObE77RwkydwF7h6DmnKqbvlalz93m/dnphUyCa6SWSP51VgtEUf2FN+gelFQ==} + engines: {node: '>=6.0.0'} + hasBin: true + + bidi-js@1.0.3: + resolution: {integrity: sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==} + + brace-expansion@5.0.6: + resolution: {integrity: sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==} + engines: {node: 18 || 20 || >=22} + + browserslist@4.28.2: + resolution: {integrity: sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + + call-bind-apply-helpers@1.0.2: + resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} + engines: {node: '>= 0.4'} + + caniuse-lite@1.0.30001792: + resolution: {integrity: sha512-hVLMUZFgR4JJ6ACt1uEESvQN1/dBVqPAKY0hgrV70eN3391K6juAfTjKZLKvOMsx8PxA7gsY1/tLMMTcfFLLpw==} + + chai@6.2.2: + resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==} + engines: {node: '>=18'} + + classnames@2.5.1: + resolution: {integrity: sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==} + + cli-width@4.1.0: + resolution: {integrity: sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==} + engines: {node: '>= 12'} + + cliui@8.0.1: + resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} + engines: {node: '>=12'} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + combined-stream@1.0.8: + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} + engines: {node: '>= 0.8'} + + convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + + cookie@1.1.1: + resolution: {integrity: sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==} + engines: {node: '>=18'} + + cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} + + css-tree@3.2.1: + resolution: {integrity: sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==} + engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0} + + css.escape@1.5.1: + resolution: {integrity: sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==} + + csstype@3.2.3: + resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} + + data-urls@7.0.0: + resolution: {integrity: sha512-23XHcCF+coGYevirZceTVD7NdJOqVn+49IHyxgszm+JIiHLoB2TkmPtsYkNWT1pvRSGkc35L6NHs0yHkN2SumA==} + engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + + dayjs@1.11.20: + resolution: {integrity: sha512-YbwwqR/uYpeoP4pu043q+LTDLFBLApUP6VxRihdfNTqu4ubqMlGDLd6ErXhEgsyvY0K6nCs7nggYumAN+9uEuQ==} + + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + decimal.js@10.6.0: + resolution: {integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==} + + deep-is@0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + + deepmerge@4.3.1: + resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} + engines: {node: '>=0.10.0'} + + delayed-stream@1.0.0: + resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} + engines: {node: '>=0.4.0'} + + dequal@2.0.3: + resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} + engines: {node: '>=6'} + + detect-libc@2.1.2: + resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} + engines: {node: '>=8'} + + dom-accessibility-api@0.5.16: + resolution: {integrity: sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==} + + dom-accessibility-api@0.6.3: + resolution: {integrity: sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==} + + dunder-proto@1.0.1: + resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} + engines: {node: '>= 0.4'} + + echarts@6.0.0: + resolution: {integrity: sha512-Tte/grDQRiETQP4xz3iZWSvoHrkCQtwqd6hs+mifXcjrCuo2iKWbajFObuLJVBlDIJlOzgQPd1hsaKt/3+OMkQ==} + + electron-to-chromium@1.5.353: + resolution: {integrity: sha512-kOrWphBi8TOZyiJZqsgqIle0lw+tzmnQK83pV9dZUd01Nm2POECSyFQMAuarzZdYqQW7FH9RaYOuaRo3h+bQ3w==} + + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + + entities@8.0.0: + resolution: {integrity: sha512-zwfzJecQ/Uej6tusMqwAqU/6KL2XaB2VZ2Jg54Je6ahNBGNH6Ek6g3jjNCF0fG9EWQKGZNddNjU5F1ZQn/sBnA==} + engines: {node: '>=20.19.0'} + + es-define-property@1.0.1: + resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} + engines: {node: '>= 0.4'} + + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + + es-module-lexer@2.1.0: + resolution: {integrity: sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ==} + + es-object-atoms@1.1.1: + resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} + engines: {node: '>= 0.4'} + + es-set-tostringtag@2.1.0: + resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} + engines: {node: '>= 0.4'} + + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + + escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + + eslint-plugin-react-hooks@7.1.1: + resolution: {integrity: sha512-f2I7Gw6JbvCexzIInuSbZpfdQ44D7iqdWX01FKLvrPgqxoE7oMj8clOfto8U6vYiz4yd5oKu39rRSVOe1zRu0g==} + engines: {node: '>=18'} + peerDependencies: + eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 || ^10.0.0 + + eslint-plugin-react-refresh@0.5.2: + resolution: {integrity: sha512-hmgTH57GfzoTFjVN0yBwTggnsVUF2tcqi7RJZHqi9lIezSs4eFyAMktA68YD4r5kNw1mxyY4dmkyoFDb3FIqrA==} + peerDependencies: + eslint: ^9 || ^10 + + eslint-scope@9.1.2: + resolution: {integrity: sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + eslint-visitor-keys@3.4.3: + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + eslint-visitor-keys@5.0.1: + resolution: {integrity: sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + eslint@10.3.0: + resolution: {integrity: sha512-XbEXaRva5cF0ZQB8w6MluHA0kZZfV2DuCMJ3ozyEOHLwDpZX2Lmm/7Pp0xdJmI0GL1W05VH5VwIFHEm1Vcw2gw==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + hasBin: true + peerDependencies: + jiti: '*' + peerDependenciesMeta: + jiti: + optional: true + + espree@11.2.0: + resolution: {integrity: sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + esquery@1.7.0: + resolution: {integrity: sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==} + engines: {node: '>=0.10'} + + esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + + estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + + estree-walker@3.0.3: + resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + + esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + + expect-type@1.3.0: + resolution: {integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==} + engines: {node: '>=12.0.0'} + + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + + fast-levenshtein@2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + + fast-string-truncated-width@3.0.3: + resolution: {integrity: sha512-0jjjIEL6+0jag3l2XWWizO64/aZVtpiGE3t0Zgqxv0DPuxiMjvB3M24fCyhZUO4KomJQPj3LTSUnDP3GpdwC0g==} + + fast-string-width@3.0.2: + resolution: {integrity: sha512-gX8LrtNEI5hq8DVUfRQMbr5lpaS4nMIWV+7XEbXk2b8kiQIizgnlr12B4dA3ZEx3308ze0O4Q1R+cHts8kyUJg==} + + fast-wrap-ansi@0.2.0: + resolution: {integrity: sha512-rLV8JHxTyhVmFYhBJuMujcrHqOT2cnO5Zxj37qROj23CP39GXubJRBUFF0z8KFK77Uc0SukZUf7JZhsVEQ6n8w==} + + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + file-entry-cache@8.0.0: + resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} + engines: {node: '>=16.0.0'} + + find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + + flat-cache@4.0.1: + resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} + engines: {node: '>=16'} + + flatted@3.4.2: + resolution: {integrity: sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==} + + follow-redirects@1.16.0: + resolution: {integrity: sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==} + engines: {node: '>=4.0'} + peerDependencies: + debug: '*' + peerDependenciesMeta: + debug: + optional: true + + form-data@4.0.5: + resolution: {integrity: sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==} + engines: {node: '>= 6'} + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + gensync@1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} + + get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + + get-intrinsic@1.3.0: + resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} + engines: {node: '>= 0.4'} + + get-proto@1.0.1: + resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} + engines: {node: '>= 0.4'} + + glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + + globals@17.6.0: + resolution: {integrity: sha512-sepffkT8stwnIYbsMBpoCHJuJM5l98FUF2AnE07hfvE0m/qp3R586hw4jF4uadbhvg1ooIdzuu7CsfD2jzCaNA==} + engines: {node: '>=18'} + + gopd@1.2.0: + resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} + engines: {node: '>= 0.4'} + + graphql@16.14.0: + resolution: {integrity: sha512-BBvQ/406p+4CZbTpCbVPSxfzrZrbnuWSP1ELYgyS6B+hNeKzgrdB4JczCa5VZUBQrDa9hUngm0KnexY6pJRN5Q==} + engines: {node: ^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0} + + has-symbols@1.1.0: + resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} + engines: {node: '>= 0.4'} + + has-tostringtag@1.0.2: + resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} + engines: {node: '>= 0.4'} + + hasown@2.0.3: + resolution: {integrity: sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==} + engines: {node: '>= 0.4'} + + headers-polyfill@5.0.1: + resolution: {integrity: sha512-1TJ6Fih/b8h5TIcv+1+Hw0PDQWJTKDKzFZzcKOiW1wJza3XoAQlkCuXLbymPYB8+ZQyw8mHvdw560e8zVFIWyA==} + + hermes-estree@0.25.1: + resolution: {integrity: sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==} + + hermes-parser@0.25.1: + resolution: {integrity: sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==} + + html-encoding-sniffer@6.0.0: + resolution: {integrity: sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg==} + engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + + ignore@5.3.2: + resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} + engines: {node: '>= 4'} + + ignore@7.0.5: + resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} + engines: {node: '>= 4'} + + imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + + indent-string@4.0.0: + resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} + engines: {node: '>=8'} + + intersection-observer@0.12.2: + resolution: {integrity: sha512-7m1vEcPCxXYI8HqnL8CKI6siDyD+eIWSwgB3DZA+ZTogxk9I4CDnj4wilt9x/+/QbHI4YG5YZNmC6458/e9Ktg==} + deprecated: The Intersection Observer polyfill is no longer needed and can safely be removed. Intersection Observer has been Baseline since 2019. + + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + + is-node-process@1.2.0: + resolution: {integrity: sha512-Vg4o6/fqPxIjtxgUH5QLJhwZ7gW5diGCVlXpuUfELC62CuxM1iHcRe51f2W1FDy04Ai4KJkagKjx3XaqyfRKXw==} + + is-potential-custom-element-name@1.0.1: + resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + js-cookie@3.0.5: + resolution: {integrity: sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==} + engines: {node: '>=14'} + + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + jsdom@29.1.1: + resolution: {integrity: sha512-ECi4Fi2f7BdJtUKTflYRTiaMxIB0O6zfR1fX0GXpUrf6flp8QIYn1UT20YQqdSOfk2dfkCwS8LAFoJDEppNK5Q==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24.0.0} + peerDependencies: + canvas: ^3.0.0 + peerDependenciesMeta: + canvas: + optional: true + + jsesc@3.1.0: + resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} + engines: {node: '>=6'} + hasBin: true + + json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + + json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + + json-stable-stringify-without-jsonify@1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + + json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + + keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + + levn@0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + + lightningcss-android-arm64@1.32.0: + resolution: {integrity: sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [android] + + lightningcss-darwin-arm64@1.32.0: + resolution: {integrity: sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [darwin] + + lightningcss-darwin-x64@1.32.0: + resolution: {integrity: sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [darwin] + + lightningcss-freebsd-x64@1.32.0: + resolution: {integrity: sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [freebsd] + + lightningcss-linux-arm-gnueabihf@1.32.0: + resolution: {integrity: sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==} + engines: {node: '>= 12.0.0'} + cpu: [arm] + os: [linux] + + lightningcss-linux-arm64-gnu@1.32.0: + resolution: {integrity: sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + lightningcss-linux-arm64-musl@1.32.0: + resolution: {integrity: sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [musl] + + lightningcss-linux-x64-gnu@1.32.0: + resolution: {integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [glibc] + + lightningcss-linux-x64-musl@1.32.0: + resolution: {integrity: sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [musl] + + lightningcss-win32-arm64-msvc@1.32.0: + resolution: {integrity: sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [win32] + + lightningcss-win32-x64-msvc@1.32.0: + resolution: {integrity: sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [win32] + + lightningcss@1.32.0: + resolution: {integrity: sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==} + engines: {node: '>= 12.0.0'} + + locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + + lodash@4.18.1: + resolution: {integrity: sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==} + + lru-cache@11.3.6: + resolution: {integrity: sha512-Gf/KoL3C/MlI7Bt0PGI9I+TeTC/I6r/csU58N4BSNc4lppLBeKsOdFYkK+dX0ABDUMJNfCHTyPpzwwO21Awd3A==} + engines: {node: 20 || >=22} + + lru-cache@5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + + lz-string@1.5.0: + resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==} + hasBin: true + + magic-string@0.30.21: + resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + + math-intrinsics@1.1.0: + resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} + engines: {node: '>= 0.4'} + + mdn-data@2.27.1: + resolution: {integrity: sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==} + + mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + + mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + + min-indent@1.0.1: + resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} + engines: {node: '>=4'} + + minimatch@10.2.5: + resolution: {integrity: sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==} + engines: {node: 18 || 20 || >=22} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + msw@2.14.5: + resolution: {integrity: sha512-X6G05oX4x0e+CNI55KMdhMmwHCBKf2iwazGr+iwsdoJ94JA1ED7wSXb6V+lLPdqFkmIlPiGYvayqnaNcOzobDA==} + engines: {node: '>=18'} + hasBin: true + peerDependencies: + typescript: '>= 4.8.x' + peerDependenciesMeta: + typescript: + optional: true + + mute-stream@3.0.0: + resolution: {integrity: sha512-dkEJPVvun4FryqBmZ5KhDo0K9iDXAwn08tMLDinNdRBNPcYEDiWYysLcc6k3mjTMlbP9KyylvRpd4wFtwrT9rw==} + engines: {node: ^20.17.0 || >=22.9.0} + + nano-memoize@3.0.16: + resolution: {integrity: sha512-JyK96AKVGAwVeMj3MoMhaSXaUNqgMbCRSQB3trUV8tYZfWEzqUBKdK1qJpfuNXgKeHOx1jv/IEYTM659ly7zUA==} + + nanoid@3.3.12: + resolution: {integrity: sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + + node-releases@2.0.38: + resolution: {integrity: sha512-3qT/88Y3FbH/Kx4szpQQ4HzUbVrHPKTLVpVocKiLfoYvw9XSGOX2FmD2d6DrXbVYyAQTF2HeF6My8jmzx7/CRw==} + + obug@2.1.1: + resolution: {integrity: sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==} + + optionator@0.9.4: + resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} + engines: {node: '>= 0.8.0'} + + outvariant@1.4.3: + resolution: {integrity: sha512-+Sl2UErvtsoajRDKCE5/dBz4DIvHXQQnAxtQTF04OJxY0+DyZXSo5P5Bb7XYWOh81syohlYL24hbDwxedPUJCA==} + + p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + + p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + + parse5@8.0.1: + resolution: {integrity: sha512-z1e/HMG90obSGeidlli3hj7cbocou0/wa5HacvI3ASx34PecNjNQeaHNo5WIZpWofN9kgkqV1q5YvXe3F0FoPw==} + + path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + path-to-regexp@6.3.0: + resolution: {integrity: sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==} + + pathe@2.0.3: + resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@4.0.4: + resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==} + engines: {node: '>=12'} + + postcss@8.5.14: + resolution: {integrity: sha512-SoSL4+OSEtR99LHFZQiJLkT59C5B1amGO1NzTwj7TT1qCUgUO6hxOvzkOYxD+vMrXBM3XJIKzokoERdqQq/Zmg==} + engines: {node: ^10 || ^12 || >=14} + + prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + + pretty-format@27.5.1: + resolution: {integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + + proxy-from-env@2.1.0: + resolution: {integrity: sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==} + engines: {node: '>=10'} + + punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + + rc-field-form@1.44.0: + resolution: {integrity: sha512-el7w87fyDUsca63Y/s8qJcq9kNkf/J5h+iTdqG5WsSHLH0e6Usl7QuYSmSVzJMgtp40mOVZIY/W/QP9zwrp1FA==} + engines: {node: '>=8.x'} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + rc-motion@2.9.5: + resolution: {integrity: sha512-w+XTUrfh7ArbYEd2582uDrEhmBHwK1ZENJiSJVb7uRxdE7qJSYjbO2eksRXmndqyKqKoYPc9ClpPh5242mV1vA==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + rc-segmented@2.4.1: + resolution: {integrity: sha512-KUi+JJFdKnumV9iXlm+BJ00O4NdVBp2TEexLCk6bK1x/RH83TvYKQMzIz/7m3UTRPD08RM/8VG/JNjWgWbd4cw==} + peerDependencies: + react: '>=16.0.0' + react-dom: '>=16.0.0' + + rc-util@5.44.4: + resolution: {integrity: sha512-resueRJzmHG9Q6rI/DfK6Kdv9/Lfls05vzMs1Sk3M2P+3cJa+MakaZyWY8IPfehVuhPJFKrIY1IK4GqbiaiY5w==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + react-dom@19.2.6: + resolution: {integrity: sha512-0prMI+hvBbPjsWnxDLxlCGyM8PN6UuWjEUCYmZhO67xIV9Xasa/r/vDnq+Xyq4Lo27g8QSbO5YzARu0D1Sps3g==} + peerDependencies: + react: ^19.2.6 + + react-fast-compare@3.2.2: + resolution: {integrity: sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==} + + react-is@17.0.2: + resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} + + react-is@18.3.1: + resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} + + react-router-dom@7.15.0: + resolution: {integrity: sha512-VcrVg64Fo8nwBvDscajG8gRTLIuTC6N50nb22l2HOOV4PTOHgoGp8mUjy9wLiHYoYTSYI36tUnXZgasSRFZorQ==} + engines: {node: '>=20.0.0'} + peerDependencies: + react: '>=18' + react-dom: '>=18' + + react-router@7.15.0: + resolution: {integrity: sha512-HW9vYwuM8f4yx66Izy8xfrzCM+SBJluoZcCbww9A1TySax11S5Vgw6fi3ZjMONw9J4gQwngL7PzkyIpJJpJ7RQ==} + engines: {node: '>=20.0.0'} + peerDependencies: + react: '>=18' + react-dom: '>=18' + peerDependenciesMeta: + react-dom: + optional: true + + react@19.2.6: + resolution: {integrity: sha512-sfWGGfavi0xr8Pg0sVsyHMAOziVYKgPLNrS7ig+ivMNb3wbCBw3KxtflsGBAwD3gYQlE/AEZsTLgToRrSCjb0Q==} + engines: {node: '>=0.10.0'} + + redent@3.0.0: + resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==} + engines: {node: '>=8'} + + require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + + require-from-string@2.0.2: + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + engines: {node: '>=0.10.0'} + + resize-observer-polyfill@1.5.1: + resolution: {integrity: sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==} + + rettime@0.11.11: + resolution: {integrity: sha512-ILJRqVWBCTlg9r42fFgwVZx1gnFAcQF8mRoMkbgQfIrjEDf9nbBFDFx00oloOa+Q869FUtaYDXZvEfnecQSCoQ==} + + rolldown@1.0.0-rc.18: + resolution: {integrity: sha512-phmyKBpuBdRYDf4hgyynGAYn/rDDe+iZXKVJ7WX5b1zQzpLkP5oJRPGsfJuHdzPMlyyEO/4sPW6yfSx2gf7lVg==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + + runes2@1.1.4: + resolution: {integrity: sha512-LNPnEDPOOU4ehF71m5JoQyzT2yxwD6ZreFJ7MxZUAoMKNMY1XrAo60H1CUoX5ncSm0rIuKlqn9JZNRrRkNou2g==} + + saxes@6.0.0: + resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==} + engines: {node: '>=v12.22.7'} + + scheduler@0.27.0: + resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==} + + screenfull@5.2.0: + resolution: {integrity: sha512-9BakfsO2aUQN2K9Fdbj87RJIEZ82Q9IGim7FqM5OsebfoFC6ZHXgDq/KvniuLTPdeM8wY2o6Dj3WQ7KeQCj3cA==} + engines: {node: '>=0.10.0'} + + semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + + semver@7.8.0: + resolution: {integrity: sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA==} + engines: {node: '>=10'} + hasBin: true + + set-cookie-parser@2.7.2: + resolution: {integrity: sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==} + + set-cookie-parser@3.1.0: + resolution: {integrity: sha512-kjnC1DXBHcxaOaOXBHBeRtltsDG2nUiUni+jP92M9gYdW12rsmx92UsfpH7o5tDRs7I1ZZPSQJQGv3UaRfCiuw==} + + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + + siginfo@2.0.0: + resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + + signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + stackback@0.0.2: + resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + + staged-components@1.1.3: + resolution: {integrity: sha512-9EIswzDqjwlEu+ymkV09TTlJfzSbKgEnNteUnZSTxkpMgr5Wx2CzzA9WcMFWBNCldqVPsHVnRGGrApduq2Se5A==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + + statuses@2.0.2: + resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==} + engines: {node: '>= 0.8'} + + std-env@4.1.0: + resolution: {integrity: sha512-Rq7ybcX2RuC55r9oaPVEW7/xu3tj8u4GeBYHBWCychFtzMIr86A7e3PPEBPT37sHStKX3+TiX/Fr/ACmJLVlLQ==} + + strict-event-emitter@0.5.1: + resolution: {integrity: sha512-vMgjE/GGEPEFnhFub6pa4FmJBRBVOLpIII2hvCZ8Kzb7K0hlHo7mQv6xYrBvCL2LtAIBwFUK8wvuJgTVSQ5MFQ==} + + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + + strip-indent@3.0.0: + resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==} + engines: {node: '>=8'} + + symbol-tree@3.2.4: + resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} + + tagged-tag@1.0.0: + resolution: {integrity: sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng==} + engines: {node: '>=20'} + + tinybench@2.9.0: + resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} + + tinyexec@1.1.2: + resolution: {integrity: sha512-dAqSqE/RabpBKI8+h26GfLq6Vb3JVXs30XYQjdMjaj/c2tS8IYYMbIzP599KtRj7c57/wYApb3QjgRgXmrCukA==} + engines: {node: '>=18'} + + tinyglobby@0.2.16: + resolution: {integrity: sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==} + engines: {node: '>=12.0.0'} + + tinyrainbow@3.1.0: + resolution: {integrity: sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==} + engines: {node: '>=14.0.0'} + + tldts-core@7.0.30: + resolution: {integrity: sha512-uiHN8PIB1VmWyS98eZYja4xzlYqeFZVjb4OuYlJQnZAuJhMw4PbKQOKgHKhBdJR3FE/t5mUQ1Kd80++B+qhD1Q==} + + tldts@7.0.30: + resolution: {integrity: sha512-ELrFxuqsDdHUwoh0XxDbxuLD3Wnz49Z57IFvTtvWy1hJdcMZjXLIuonjilCiWHlT2GbE4Wlv1wKVTzDFnXH1aw==} + hasBin: true + + tough-cookie@6.0.1: + resolution: {integrity: sha512-LktZQb3IeoUWB9lqR5EWTHgW/VTITCXg4D21M+lvybRVdylLrRMnqaIONLVb5mav8vM19m44HIcGq4qASeu2Qw==} + engines: {node: '>=16'} + + tr46@6.0.0: + resolution: {integrity: sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==} + engines: {node: '>=20'} + + ts-api-utils@2.5.0: + resolution: {integrity: sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==} + engines: {node: '>=18.12'} + peerDependencies: + typescript: '>=4.8.4' + + tslib@2.3.0: + resolution: {integrity: sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==} + + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + + type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + + type-fest@5.6.0: + resolution: {integrity: sha512-8ZiHFm91orbSAe2PSAiSVBVko18pbhbiB3U9GglSzF/zCGkR+rxpHx6sEMCUm4kxY4LjDIUGgCfUMtwfZfjfUA==} + engines: {node: '>=20'} + + typescript-eslint@8.59.2: + resolution: {integrity: sha512-pJw051uomb3ZeCzGTpRb8RbEqB5Y4WWet8gl/GcTlU35BSx0PVdZ86/bqkQCyKKuraVQEK7r6kBHQXF+fBhkoQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + typescript@6.0.3: + resolution: {integrity: sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==} + engines: {node: '>=14.17'} + hasBin: true + + undici-types@7.16.0: + resolution: {integrity: sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==} + + undici@7.25.0: + resolution: {integrity: sha512-xXnp4kTyor2Zq+J1FfPI6Eq3ew5h6Vl0F/8d9XU5zZQf1tX9s2Su1/3PiMmUANFULpmksxkClamIZcaUqryHsQ==} + engines: {node: '>=20.18.1'} + + until-async@3.0.2: + resolution: {integrity: sha512-IiSk4HlzAMqTUseHHe3VhIGyuFmN90zMTpD3Z3y8jeQbzLIq500MVM7Jq2vUAnTKAFPJrqwkzr6PoTcPhGcOiw==} + + update-browserslist-db@1.2.3: + resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + + uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + + use-sync-external-store@1.6.0: + resolution: {integrity: sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + vite@8.0.11: + resolution: {integrity: sha512-Jz1mxtUBR5xTT65VOdJZUUeoyLtqljmFkiUXhPTLZka3RDc9vpi/xXkyrnsdRcm2lIi3l3GPMnAidTsEGIj3Ow==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + '@types/node': ^20.19.0 || >=22.12.0 + '@vitejs/devtools': ^0.1.18 + esbuild: ^0.27.0 || ^0.28.0 + jiti: '>=1.21.0' + less: ^4.0.0 + sass: ^1.70.0 + sass-embedded: ^1.70.0 + stylus: '>=0.54.8' + sugarss: ^5.0.0 + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + '@types/node': + optional: true + '@vitejs/devtools': + optional: true + esbuild: + optional: true + jiti: + optional: true + less: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + + vitest@4.1.5: + resolution: {integrity: sha512-9Xx1v3/ih3m9hN+SbfkUyy0JAs72ap3r7joc87XL6jwF0jGg6mFBvQ1SrwaX+h8BlkX6Hz9shdd1uo6AF+ZGpg==} + engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@opentelemetry/api': ^1.9.0 + '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0 + '@vitest/browser-playwright': 4.1.5 + '@vitest/browser-preview': 4.1.5 + '@vitest/browser-webdriverio': 4.1.5 + '@vitest/coverage-istanbul': 4.1.5 + '@vitest/coverage-v8': 4.1.5 + '@vitest/ui': 4.1.5 + happy-dom: '*' + jsdom: '*' + vite: ^6.0.0 || ^7.0.0 || ^8.0.0 + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@opentelemetry/api': + optional: true + '@types/node': + optional: true + '@vitest/browser-playwright': + optional: true + '@vitest/browser-preview': + optional: true + '@vitest/browser-webdriverio': + optional: true + '@vitest/coverage-istanbul': + optional: true + '@vitest/coverage-v8': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + + w3c-xmlserializer@5.0.0: + resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==} + engines: {node: '>=18'} + + webidl-conversions@8.0.1: + resolution: {integrity: sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==} + engines: {node: '>=20'} + + whatwg-mimetype@5.0.0: + resolution: {integrity: sha512-sXcNcHOC51uPGF0P/D4NVtrkjSU2fNsm9iog4ZvZJsL3rjoDAzXZhkm2MWt1y+PUdggKAYVoMAIYcs78wJ51Cw==} + engines: {node: '>=20'} + + whatwg-url@16.0.1: + resolution: {integrity: sha512-1to4zXBxmXHV3IiSSEInrreIlu02vUOvrhxJJH5vcxYTBDAx51cqZiKdyTxlecdKNSjj8EcxGBxNf6Vg+945gw==} + engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + + why-is-node-running@2.3.0: + resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} + engines: {node: '>=8'} + hasBin: true + + word-wrap@1.2.5: + resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} + engines: {node: '>=0.10.0'} + + wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + + xml-name-validator@5.0.0: + resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==} + engines: {node: '>=18'} + + xmlchars@2.2.0: + resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==} + + y18n@5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + + yallist@3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + + yargs-parser@21.1.1: + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} + + yargs@17.7.2: + resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} + engines: {node: '>=12'} + + yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + + zod-validation-error@4.0.2: + resolution: {integrity: sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==} + engines: {node: '>=18.0.0'} + peerDependencies: + zod: ^3.25.0 || ^4.0.0 + + zod@4.4.3: + resolution: {integrity: sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==} + + zrender@6.0.0: + resolution: {integrity: sha512-41dFXEEXuJpNecuUQq6JlbybmnHaqqpGlbH1yxnA5V9MMP4SbohSVZsJIwz+zdjQXSSlR1Vc34EgH1zxyTDvhg==} + + zustand@5.0.13: + resolution: {integrity: sha512-efI2tVaVQPqtOh114loML/Z80Y4NP3yc+Ff0fYiZJPauNeWZeIp/bRFD7I9bfmCOYBh/PHxlglQ9+wvlwnPikQ==} + engines: {node: '>=12.20.0'} + peerDependencies: + '@types/react': '>=18.0.0' + immer: '>=9.0.6' + react: '>=18.0.0' + use-sync-external-store: '>=1.2.0' + peerDependenciesMeta: + '@types/react': + optional: true + immer: + optional: true + react: + optional: true + use-sync-external-store: + optional: true + +snapshots: + + '@adobe/css-tools@4.4.4': {} + + '@asamuzakjp/css-color@5.1.11': + dependencies: + '@asamuzakjp/generational-cache': 1.0.1 + '@csstools/css-calc': 3.2.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + '@csstools/css-color-parser': 4.1.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) + '@csstools/css-tokenizer': 4.0.0 + + '@asamuzakjp/dom-selector@7.1.1': + dependencies: + '@asamuzakjp/generational-cache': 1.0.1 + '@asamuzakjp/nwsapi': 2.3.9 + bidi-js: 1.0.3 + css-tree: 3.2.1 + is-potential-custom-element-name: 1.0.1 + + '@asamuzakjp/generational-cache@1.0.1': {} + + '@asamuzakjp/nwsapi@2.3.9': {} + + '@babel/code-frame@7.29.0': + dependencies: + '@babel/helper-validator-identifier': 7.28.5 + js-tokens: 4.0.0 + picocolors: 1.1.1 + + '@babel/compat-data@7.29.3': {} + + '@babel/core@7.29.0': + dependencies: + '@babel/code-frame': 7.29.0 + '@babel/generator': 7.29.1 + '@babel/helper-compilation-targets': 7.28.6 + '@babel/helper-module-transforms': 7.28.6(@babel/core@7.29.0) + '@babel/helpers': 7.29.2 + '@babel/parser': 7.29.3 + '@babel/template': 7.28.6 + '@babel/traverse': 7.29.0 + '@babel/types': 7.29.0 + '@jridgewell/remapping': 2.3.5 + convert-source-map: 2.0.0 + debug: 4.4.3 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/generator@7.29.1': + dependencies: + '@babel/parser': 7.29.3 + '@babel/types': 7.29.0 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + jsesc: 3.1.0 + + '@babel/helper-compilation-targets@7.28.6': + dependencies: + '@babel/compat-data': 7.29.3 + '@babel/helper-validator-option': 7.27.1 + browserslist: 4.28.2 + lru-cache: 5.1.1 + semver: 6.3.1 + + '@babel/helper-globals@7.28.0': {} + + '@babel/helper-module-imports@7.28.6': + dependencies: + '@babel/traverse': 7.29.0 + '@babel/types': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-transforms@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-module-imports': 7.28.6 + '@babel/helper-validator-identifier': 7.28.5 + '@babel/traverse': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/helper-string-parser@7.27.1': {} + + '@babel/helper-validator-identifier@7.28.5': {} + + '@babel/helper-validator-option@7.27.1': {} + + '@babel/helpers@7.29.2': + dependencies: + '@babel/template': 7.28.6 + '@babel/types': 7.29.0 + + '@babel/parser@7.29.3': + dependencies: + '@babel/types': 7.29.0 + + '@babel/runtime@7.29.2': {} + + '@babel/template@7.28.6': + dependencies: + '@babel/code-frame': 7.29.0 + '@babel/parser': 7.29.3 + '@babel/types': 7.29.0 + + '@babel/traverse@7.29.0': + dependencies: + '@babel/code-frame': 7.29.0 + '@babel/generator': 7.29.1 + '@babel/helper-globals': 7.28.0 + '@babel/parser': 7.29.3 + '@babel/template': 7.28.6 + '@babel/types': 7.29.0 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + '@babel/types@7.29.0': + dependencies: + '@babel/helper-string-parser': 7.27.1 + '@babel/helper-validator-identifier': 7.28.5 + + '@bramus/specificity@2.4.2': + dependencies: + css-tree: 3.2.1 + + '@csstools/color-helpers@6.0.2': {} + + '@csstools/css-calc@3.2.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)': + dependencies: + '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) + '@csstools/css-tokenizer': 4.0.0 + + '@csstools/css-color-parser@4.1.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)': + dependencies: + '@csstools/color-helpers': 6.0.2 + '@csstools/css-calc': 3.2.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) + '@csstools/css-tokenizer': 4.0.0 + + '@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0)': + dependencies: + '@csstools/css-tokenizer': 4.0.0 + + '@csstools/css-syntax-patches-for-csstree@1.1.3(css-tree@3.2.1)': + optionalDependencies: + css-tree: 3.2.1 + + '@csstools/css-tokenizer@4.0.0': {} + + '@emnapi/core@1.10.0': + dependencies: + '@emnapi/wasi-threads': 1.2.1 + tslib: 2.8.1 + optional: true + + '@emnapi/runtime@1.10.0': + dependencies: + tslib: 2.8.1 + optional: true + + '@emnapi/wasi-threads@1.2.1': + dependencies: + tslib: 2.8.1 + optional: true + + '@eslint-community/eslint-utils@4.9.1(eslint@10.3.0)': + dependencies: + eslint: 10.3.0 + eslint-visitor-keys: 3.4.3 + + '@eslint-community/regexpp@4.12.2': {} + + '@eslint/config-array@0.23.5': + dependencies: + '@eslint/object-schema': 3.0.5 + debug: 4.4.3 + minimatch: 10.2.5 + transitivePeerDependencies: + - supports-color + + '@eslint/config-helpers@0.5.5': + dependencies: + '@eslint/core': 1.2.1 + + '@eslint/core@1.2.1': + dependencies: + '@types/json-schema': 7.0.15 + + '@eslint/js@10.0.1(eslint@10.3.0)': + optionalDependencies: + eslint: 10.3.0 + + '@eslint/object-schema@3.0.5': {} + + '@eslint/plugin-kit@0.7.1': + dependencies: + '@eslint/core': 1.2.1 + levn: 0.4.1 + + '@exodus/bytes@1.15.0': {} + + '@floating-ui/core@1.7.5': + dependencies: + '@floating-ui/utils': 0.2.11 + + '@floating-ui/dom@1.7.6': + dependencies: + '@floating-ui/core': 1.7.5 + '@floating-ui/utils': 0.2.11 + + '@floating-ui/utils@0.2.11': {} + + '@humanfs/core@0.19.2': + dependencies: + '@humanfs/types': 0.15.0 + + '@humanfs/node@0.16.8': + dependencies: + '@humanfs/core': 0.19.2 + '@humanfs/types': 0.15.0 + '@humanwhocodes/retry': 0.4.3 + + '@humanfs/types@0.15.0': {} + + '@humanwhocodes/module-importer@1.0.1': {} + + '@humanwhocodes/retry@0.4.3': {} + + '@inquirer/ansi@2.0.5': {} + + '@inquirer/confirm@6.0.13(@types/node@24.12.3)': + dependencies: + '@inquirer/core': 11.1.10(@types/node@24.12.3) + '@inquirer/type': 4.0.5(@types/node@24.12.3) + optionalDependencies: + '@types/node': 24.12.3 + + '@inquirer/core@11.1.10(@types/node@24.12.3)': + dependencies: + '@inquirer/ansi': 2.0.5 + '@inquirer/figures': 2.0.5 + '@inquirer/type': 4.0.5(@types/node@24.12.3) + cli-width: 4.1.0 + fast-wrap-ansi: 0.2.0 + mute-stream: 3.0.0 + signal-exit: 4.1.0 + optionalDependencies: + '@types/node': 24.12.3 + + '@inquirer/figures@2.0.5': {} + + '@inquirer/type@4.0.5(@types/node@24.12.3)': + optionalDependencies: + '@types/node': 24.12.3 + + '@jridgewell/gen-mapping@0.3.13': + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/remapping@2.3.5': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/sourcemap-codec@1.5.5': {} + + '@jridgewell/trace-mapping@0.3.31': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.5 + + '@mswjs/interceptors@0.41.8': + dependencies: + '@open-draft/deferred-promise': 2.2.0 + '@open-draft/logger': 0.3.0 + '@open-draft/until': 2.1.0 + is-node-process: 1.2.0 + outvariant: 1.4.3 + strict-event-emitter: 0.5.1 + + '@napi-rs/wasm-runtime@1.1.4(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)': + dependencies: + '@emnapi/core': 1.10.0 + '@emnapi/runtime': 1.10.0 + '@tybys/wasm-util': 0.10.2 + optional: true + + '@open-draft/deferred-promise@2.2.0': {} + + '@open-draft/deferred-promise@3.0.0': {} + + '@open-draft/logger@0.3.0': + dependencies: + is-node-process: 1.2.0 + outvariant: 1.4.3 + + '@open-draft/until@2.1.0': {} + + '@oxc-project/types@0.128.0': {} + + '@rc-component/mini-decimal@1.1.3': + dependencies: + '@babel/runtime': 7.29.2 + + '@react-spring/animated@9.6.1(react@19.2.6)': + dependencies: + '@react-spring/shared': 9.6.1(react@19.2.6) + '@react-spring/types': 9.6.1 + react: 19.2.6 + + '@react-spring/core@9.6.1(react@19.2.6)': + dependencies: + '@react-spring/animated': 9.6.1(react@19.2.6) + '@react-spring/rafz': 9.6.1 + '@react-spring/shared': 9.6.1(react@19.2.6) + '@react-spring/types': 9.6.1 + react: 19.2.6 + + '@react-spring/rafz@9.6.1': {} + + '@react-spring/shared@9.6.1(react@19.2.6)': + dependencies: + '@react-spring/rafz': 9.6.1 + '@react-spring/types': 9.6.1 + react: 19.2.6 + + '@react-spring/types@9.6.1': {} + + '@react-spring/web@9.6.1(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': + dependencies: + '@react-spring/animated': 9.6.1(react@19.2.6) + '@react-spring/core': 9.6.1(react@19.2.6) + '@react-spring/shared': 9.6.1(react@19.2.6) + '@react-spring/types': 9.6.1 + react: 19.2.6 + react-dom: 19.2.6(react@19.2.6) + + '@rolldown/binding-android-arm64@1.0.0-rc.18': + optional: true + + '@rolldown/binding-darwin-arm64@1.0.0-rc.18': + optional: true + + '@rolldown/binding-darwin-x64@1.0.0-rc.18': + optional: true + + '@rolldown/binding-freebsd-x64@1.0.0-rc.18': + optional: true + + '@rolldown/binding-linux-arm-gnueabihf@1.0.0-rc.18': + optional: true + + '@rolldown/binding-linux-arm64-gnu@1.0.0-rc.18': + optional: true + + '@rolldown/binding-linux-arm64-musl@1.0.0-rc.18': + optional: true + + '@rolldown/binding-linux-ppc64-gnu@1.0.0-rc.18': + optional: true + + '@rolldown/binding-linux-s390x-gnu@1.0.0-rc.18': + optional: true + + '@rolldown/binding-linux-x64-gnu@1.0.0-rc.18': + optional: true + + '@rolldown/binding-linux-x64-musl@1.0.0-rc.18': + optional: true + + '@rolldown/binding-openharmony-arm64@1.0.0-rc.18': + optional: true + + '@rolldown/binding-wasm32-wasi@1.0.0-rc.18': + dependencies: + '@emnapi/core': 1.10.0 + '@emnapi/runtime': 1.10.0 + '@napi-rs/wasm-runtime': 1.1.4(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) + optional: true + + '@rolldown/binding-win32-arm64-msvc@1.0.0-rc.18': + optional: true + + '@rolldown/binding-win32-x64-msvc@1.0.0-rc.18': + optional: true + + '@rolldown/pluginutils@1.0.0-rc.18': {} + + '@rolldown/pluginutils@1.0.0-rc.7': {} + + '@standard-schema/spec@1.1.0': {} + + '@tanstack/query-core@5.100.9': {} + + '@tanstack/react-query@5.100.9(react@19.2.6)': + dependencies: + '@tanstack/query-core': 5.100.9 + react: 19.2.6 + + '@testing-library/dom@10.4.1': + dependencies: + '@babel/code-frame': 7.29.0 + '@babel/runtime': 7.29.2 + '@types/aria-query': 5.0.4 + aria-query: 5.3.0 + dom-accessibility-api: 0.5.16 + lz-string: 1.5.0 + picocolors: 1.1.1 + pretty-format: 27.5.1 + + '@testing-library/jest-dom@6.9.1': + dependencies: + '@adobe/css-tools': 4.4.4 + aria-query: 5.3.2 + css.escape: 1.5.1 + dom-accessibility-api: 0.6.3 + picocolors: 1.1.1 + redent: 3.0.0 + + '@testing-library/react@16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': + dependencies: + '@babel/runtime': 7.29.2 + '@testing-library/dom': 10.4.1 + react: 19.2.6 + react-dom: 19.2.6(react@19.2.6) + optionalDependencies: + '@types/react': 19.2.14 + '@types/react-dom': 19.2.3(@types/react@19.2.14) + + '@testing-library/user-event@14.6.1(@testing-library/dom@10.4.1)': + dependencies: + '@testing-library/dom': 10.4.1 + + '@tybys/wasm-util@0.10.2': + dependencies: + tslib: 2.8.1 + optional: true + + '@types/aria-query@5.0.4': {} + + '@types/chai@5.2.3': + dependencies: + '@types/deep-eql': 4.0.2 + assertion-error: 2.0.1 + + '@types/deep-eql@4.0.2': {} + + '@types/esrecurse@4.3.1': {} + + '@types/estree@1.0.9': {} + + '@types/js-cookie@3.0.6': {} + + '@types/json-schema@7.0.15': {} + + '@types/node@24.12.3': + dependencies: + undici-types: 7.16.0 + + '@types/react-dom@19.2.3(@types/react@19.2.14)': + dependencies: + '@types/react': 19.2.14 + + '@types/react@19.2.14': + dependencies: + csstype: 3.2.3 + + '@types/set-cookie-parser@2.4.10': + dependencies: + '@types/node': 24.12.3 + + '@types/statuses@2.0.6': {} + + '@typescript-eslint/eslint-plugin@8.59.2(@typescript-eslint/parser@8.59.2(eslint@10.3.0)(typescript@6.0.3))(eslint@10.3.0)(typescript@6.0.3)': + dependencies: + '@eslint-community/regexpp': 4.12.2 + '@typescript-eslint/parser': 8.59.2(eslint@10.3.0)(typescript@6.0.3) + '@typescript-eslint/scope-manager': 8.59.2 + '@typescript-eslint/type-utils': 8.59.2(eslint@10.3.0)(typescript@6.0.3) + '@typescript-eslint/utils': 8.59.2(eslint@10.3.0)(typescript@6.0.3) + '@typescript-eslint/visitor-keys': 8.59.2 + eslint: 10.3.0 + ignore: 7.0.5 + natural-compare: 1.4.0 + ts-api-utils: 2.5.0(typescript@6.0.3) + typescript: 6.0.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/parser@8.59.2(eslint@10.3.0)(typescript@6.0.3)': + dependencies: + '@typescript-eslint/scope-manager': 8.59.2 + '@typescript-eslint/types': 8.59.2 + '@typescript-eslint/typescript-estree': 8.59.2(typescript@6.0.3) + '@typescript-eslint/visitor-keys': 8.59.2 + debug: 4.4.3 + eslint: 10.3.0 + typescript: 6.0.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/project-service@8.59.2(typescript@6.0.3)': + dependencies: + '@typescript-eslint/tsconfig-utils': 8.59.2(typescript@6.0.3) + '@typescript-eslint/types': 8.59.2 + debug: 4.4.3 + typescript: 6.0.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/scope-manager@8.59.2': + dependencies: + '@typescript-eslint/types': 8.59.2 + '@typescript-eslint/visitor-keys': 8.59.2 + + '@typescript-eslint/tsconfig-utils@8.59.2(typescript@6.0.3)': + dependencies: + typescript: 6.0.3 + + '@typescript-eslint/type-utils@8.59.2(eslint@10.3.0)(typescript@6.0.3)': + dependencies: + '@typescript-eslint/types': 8.59.2 + '@typescript-eslint/typescript-estree': 8.59.2(typescript@6.0.3) + '@typescript-eslint/utils': 8.59.2(eslint@10.3.0)(typescript@6.0.3) + debug: 4.4.3 + eslint: 10.3.0 + ts-api-utils: 2.5.0(typescript@6.0.3) + typescript: 6.0.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/types@8.59.2': {} + + '@typescript-eslint/typescript-estree@8.59.2(typescript@6.0.3)': + dependencies: + '@typescript-eslint/project-service': 8.59.2(typescript@6.0.3) + '@typescript-eslint/tsconfig-utils': 8.59.2(typescript@6.0.3) + '@typescript-eslint/types': 8.59.2 + '@typescript-eslint/visitor-keys': 8.59.2 + debug: 4.4.3 + minimatch: 10.2.5 + semver: 7.8.0 + tinyglobby: 0.2.16 + ts-api-utils: 2.5.0(typescript@6.0.3) + typescript: 6.0.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/utils@8.59.2(eslint@10.3.0)(typescript@6.0.3)': + dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@10.3.0) + '@typescript-eslint/scope-manager': 8.59.2 + '@typescript-eslint/types': 8.59.2 + '@typescript-eslint/typescript-estree': 8.59.2(typescript@6.0.3) + eslint: 10.3.0 + typescript: 6.0.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/visitor-keys@8.59.2': + dependencies: + '@typescript-eslint/types': 8.59.2 + eslint-visitor-keys: 5.0.1 + + '@use-gesture/core@10.3.0': {} + + '@use-gesture/react@10.3.0(react@19.2.6)': + dependencies: + '@use-gesture/core': 10.3.0 + react: 19.2.6 + + '@vitejs/plugin-react@6.0.1(vite@8.0.11(@types/node@24.12.3))': + dependencies: + '@rolldown/pluginutils': 1.0.0-rc.7 + vite: 8.0.11(@types/node@24.12.3) + + '@vitest/expect@4.1.5': + dependencies: + '@standard-schema/spec': 1.1.0 + '@types/chai': 5.2.3 + '@vitest/spy': 4.1.5 + '@vitest/utils': 4.1.5 + chai: 6.2.2 + tinyrainbow: 3.1.0 + + '@vitest/mocker@4.1.5(msw@2.14.5(@types/node@24.12.3)(typescript@6.0.3))(vite@8.0.11(@types/node@24.12.3))': + dependencies: + '@vitest/spy': 4.1.5 + estree-walker: 3.0.3 + magic-string: 0.30.21 + optionalDependencies: + msw: 2.14.5(@types/node@24.12.3)(typescript@6.0.3) + vite: 8.0.11(@types/node@24.12.3) + + '@vitest/pretty-format@4.1.5': + dependencies: + tinyrainbow: 3.1.0 + + '@vitest/runner@4.1.5': + dependencies: + '@vitest/utils': 4.1.5 + pathe: 2.0.3 + + '@vitest/snapshot@4.1.5': + dependencies: + '@vitest/pretty-format': 4.1.5 + '@vitest/utils': 4.1.5 + magic-string: 0.30.21 + pathe: 2.0.3 + + '@vitest/spy@4.1.5': {} + + '@vitest/utils@4.1.5': + dependencies: + '@vitest/pretty-format': 4.1.5 + convert-source-map: 2.0.0 + tinyrainbow: 3.1.0 + + acorn-jsx@5.3.2(acorn@8.16.0): + dependencies: + acorn: 8.16.0 + + acorn@8.16.0: {} + + ahooks@3.9.7(react-dom@19.2.6(react@19.2.6))(react@19.2.6): + dependencies: + '@babel/runtime': 7.29.2 + '@types/js-cookie': 3.0.6 + dayjs: 1.11.20 + intersection-observer: 0.12.2 + js-cookie: 3.0.5 + lodash: 4.18.1 + react: 19.2.6 + react-dom: 19.2.6(react@19.2.6) + react-fast-compare: 3.2.2 + resize-observer-polyfill: 1.5.1 + screenfull: 5.2.0 + tslib: 2.8.1 + + ajv@6.15.0: + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + + ansi-regex@5.0.1: {} + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + ansi-styles@5.2.0: {} + + antd-mobile-icons@0.3.0: {} + + antd-mobile-v5-count@1.0.1: {} + + antd-mobile@5.42.3(react-dom@19.2.6(react@19.2.6))(react@19.2.6): + dependencies: + '@floating-ui/dom': 1.7.6 + '@rc-component/mini-decimal': 1.1.3 + '@react-spring/web': 9.6.1(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@use-gesture/react': 10.3.0(react@19.2.6) + ahooks: 3.9.7(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + antd-mobile-icons: 0.3.0 + antd-mobile-v5-count: 1.0.1 + classnames: 2.5.1 + dayjs: 1.11.20 + deepmerge: 4.3.1 + nano-memoize: 3.0.16 + rc-field-form: 1.44.0(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + rc-segmented: 2.4.1(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + rc-util: 5.44.4(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + react: 19.2.6 + react-dom: 19.2.6(react@19.2.6) + react-fast-compare: 3.2.2 + react-is: 18.3.1 + runes2: 1.1.4 + staged-components: 1.1.3(react@19.2.6) + tslib: 2.8.1 + use-sync-external-store: 1.6.0(react@19.2.6) + + aria-query@5.3.0: + dependencies: + dequal: 2.0.3 + + aria-query@5.3.2: {} + + assertion-error@2.0.1: {} + + async-validator@4.2.5: {} + + asynckit@0.4.0: {} + + axios@1.16.0: + dependencies: + follow-redirects: 1.16.0 + form-data: 4.0.5 + proxy-from-env: 2.1.0 + transitivePeerDependencies: + - debug + + balanced-match@4.0.4: {} + + baseline-browser-mapping@2.10.29: {} + + bidi-js@1.0.3: + dependencies: + require-from-string: 2.0.2 + + brace-expansion@5.0.6: + dependencies: + balanced-match: 4.0.4 + + browserslist@4.28.2: + dependencies: + baseline-browser-mapping: 2.10.29 + caniuse-lite: 1.0.30001792 + electron-to-chromium: 1.5.353 + node-releases: 2.0.38 + update-browserslist-db: 1.2.3(browserslist@4.28.2) + + call-bind-apply-helpers@1.0.2: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + + caniuse-lite@1.0.30001792: {} + + chai@6.2.2: {} + + classnames@2.5.1: {} + + cli-width@4.1.0: {} + + cliui@8.0.1: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + + combined-stream@1.0.8: + dependencies: + delayed-stream: 1.0.0 + + convert-source-map@2.0.0: {} + + cookie@1.1.1: {} + + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + css-tree@3.2.1: + dependencies: + mdn-data: 2.27.1 + source-map-js: 1.2.1 + + css.escape@1.5.1: {} + + csstype@3.2.3: {} + + data-urls@7.0.0: + dependencies: + whatwg-mimetype: 5.0.0 + whatwg-url: 16.0.1 + transitivePeerDependencies: + - '@noble/hashes' + + dayjs@1.11.20: {} + + debug@4.4.3: + dependencies: + ms: 2.1.3 + + decimal.js@10.6.0: {} + + deep-is@0.1.4: {} + + deepmerge@4.3.1: {} + + delayed-stream@1.0.0: {} + + dequal@2.0.3: {} + + detect-libc@2.1.2: {} + + dom-accessibility-api@0.5.16: {} + + dom-accessibility-api@0.6.3: {} + + dunder-proto@1.0.1: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-errors: 1.3.0 + gopd: 1.2.0 + + echarts@6.0.0: + dependencies: + tslib: 2.3.0 + zrender: 6.0.0 + + electron-to-chromium@1.5.353: {} + + emoji-regex@8.0.0: {} + + entities@8.0.0: {} + + es-define-property@1.0.1: {} + + es-errors@1.3.0: {} + + es-module-lexer@2.1.0: {} + + es-object-atoms@1.1.1: + dependencies: + es-errors: 1.3.0 + + es-set-tostringtag@2.1.0: + dependencies: + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + has-tostringtag: 1.0.2 + hasown: 2.0.3 + + escalade@3.2.0: {} + + escape-string-regexp@4.0.0: {} + + eslint-plugin-react-hooks@7.1.1(eslint@10.3.0): + dependencies: + '@babel/core': 7.29.0 + '@babel/parser': 7.29.3 + eslint: 10.3.0 + hermes-parser: 0.25.1 + zod: 4.4.3 + zod-validation-error: 4.0.2(zod@4.4.3) + transitivePeerDependencies: + - supports-color + + eslint-plugin-react-refresh@0.5.2(eslint@10.3.0): + dependencies: + eslint: 10.3.0 + + eslint-scope@9.1.2: + dependencies: + '@types/esrecurse': 4.3.1 + '@types/estree': 1.0.9 + esrecurse: 4.3.0 + estraverse: 5.3.0 + + eslint-visitor-keys@3.4.3: {} + + eslint-visitor-keys@5.0.1: {} + + eslint@10.3.0: + dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@10.3.0) + '@eslint-community/regexpp': 4.12.2 + '@eslint/config-array': 0.23.5 + '@eslint/config-helpers': 0.5.5 + '@eslint/core': 1.2.1 + '@eslint/plugin-kit': 0.7.1 + '@humanfs/node': 0.16.8 + '@humanwhocodes/module-importer': 1.0.1 + '@humanwhocodes/retry': 0.4.3 + '@types/estree': 1.0.9 + ajv: 6.15.0 + cross-spawn: 7.0.6 + debug: 4.4.3 + escape-string-regexp: 4.0.0 + eslint-scope: 9.1.2 + eslint-visitor-keys: 5.0.1 + espree: 11.2.0 + esquery: 1.7.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 8.0.0 + find-up: 5.0.0 + glob-parent: 6.0.2 + ignore: 5.3.2 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + json-stable-stringify-without-jsonify: 1.0.1 + minimatch: 10.2.5 + natural-compare: 1.4.0 + optionator: 0.9.4 + transitivePeerDependencies: + - supports-color + + espree@11.2.0: + dependencies: + acorn: 8.16.0 + acorn-jsx: 5.3.2(acorn@8.16.0) + eslint-visitor-keys: 5.0.1 + + esquery@1.7.0: + dependencies: + estraverse: 5.3.0 + + esrecurse@4.3.0: + dependencies: + estraverse: 5.3.0 + + estraverse@5.3.0: {} + + estree-walker@3.0.3: + dependencies: + '@types/estree': 1.0.9 + + esutils@2.0.3: {} + + expect-type@1.3.0: {} + + fast-deep-equal@3.1.3: {} + + fast-json-stable-stringify@2.1.0: {} + + fast-levenshtein@2.0.6: {} + + fast-string-truncated-width@3.0.3: {} + + fast-string-width@3.0.2: + dependencies: + fast-string-truncated-width: 3.0.3 + + fast-wrap-ansi@0.2.0: + dependencies: + fast-string-width: 3.0.2 + + fdir@6.5.0(picomatch@4.0.4): + optionalDependencies: + picomatch: 4.0.4 + + file-entry-cache@8.0.0: + dependencies: + flat-cache: 4.0.1 + + find-up@5.0.0: + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + + flat-cache@4.0.1: + dependencies: + flatted: 3.4.2 + keyv: 4.5.4 + + flatted@3.4.2: {} + + follow-redirects@1.16.0: {} + + form-data@4.0.5: + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + es-set-tostringtag: 2.1.0 + hasown: 2.0.3 + mime-types: 2.1.35 + + fsevents@2.3.3: + optional: true + + function-bind@1.1.2: {} + + gensync@1.0.0-beta.2: {} + + get-caller-file@2.0.5: {} + + get-intrinsic@1.3.0: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + function-bind: 1.1.2 + get-proto: 1.0.1 + gopd: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.3 + math-intrinsics: 1.1.0 + + get-proto@1.0.1: + dependencies: + dunder-proto: 1.0.1 + es-object-atoms: 1.1.1 + + glob-parent@6.0.2: + dependencies: + is-glob: 4.0.3 + + globals@17.6.0: {} + + gopd@1.2.0: {} + + graphql@16.14.0: {} + + has-symbols@1.1.0: {} + + has-tostringtag@1.0.2: + dependencies: + has-symbols: 1.1.0 + + hasown@2.0.3: + dependencies: + function-bind: 1.1.2 + + headers-polyfill@5.0.1: + dependencies: + '@types/set-cookie-parser': 2.4.10 + set-cookie-parser: 3.1.0 + + hermes-estree@0.25.1: {} + + hermes-parser@0.25.1: + dependencies: + hermes-estree: 0.25.1 + + html-encoding-sniffer@6.0.0: + dependencies: + '@exodus/bytes': 1.15.0 + transitivePeerDependencies: + - '@noble/hashes' + + ignore@5.3.2: {} + + ignore@7.0.5: {} + + imurmurhash@0.1.4: {} + + indent-string@4.0.0: {} + + intersection-observer@0.12.2: {} + + is-extglob@2.1.1: {} + + is-fullwidth-code-point@3.0.0: {} + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + is-node-process@1.2.0: {} + + is-potential-custom-element-name@1.0.1: {} + + isexe@2.0.0: {} + + js-cookie@3.0.5: {} + + js-tokens@4.0.0: {} + + jsdom@29.1.1: + dependencies: + '@asamuzakjp/css-color': 5.1.11 + '@asamuzakjp/dom-selector': 7.1.1 + '@bramus/specificity': 2.4.2 + '@csstools/css-syntax-patches-for-csstree': 1.1.3(css-tree@3.2.1) + '@exodus/bytes': 1.15.0 + css-tree: 3.2.1 + data-urls: 7.0.0 + decimal.js: 10.6.0 + html-encoding-sniffer: 6.0.0 + is-potential-custom-element-name: 1.0.1 + lru-cache: 11.3.6 + parse5: 8.0.1 + saxes: 6.0.0 + symbol-tree: 3.2.4 + tough-cookie: 6.0.1 + undici: 7.25.0 + w3c-xmlserializer: 5.0.0 + webidl-conversions: 8.0.1 + whatwg-mimetype: 5.0.0 + whatwg-url: 16.0.1 + xml-name-validator: 5.0.0 + transitivePeerDependencies: + - '@noble/hashes' + + jsesc@3.1.0: {} + + json-buffer@3.0.1: {} + + json-schema-traverse@0.4.1: {} + + json-stable-stringify-without-jsonify@1.0.1: {} + + json5@2.2.3: {} + + keyv@4.5.4: + dependencies: + json-buffer: 3.0.1 + + levn@0.4.1: + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + + lightningcss-android-arm64@1.32.0: + optional: true + + lightningcss-darwin-arm64@1.32.0: + optional: true + + lightningcss-darwin-x64@1.32.0: + optional: true + + lightningcss-freebsd-x64@1.32.0: + optional: true + + lightningcss-linux-arm-gnueabihf@1.32.0: + optional: true + + lightningcss-linux-arm64-gnu@1.32.0: + optional: true + + lightningcss-linux-arm64-musl@1.32.0: + optional: true + + lightningcss-linux-x64-gnu@1.32.0: + optional: true + + lightningcss-linux-x64-musl@1.32.0: + optional: true + + lightningcss-win32-arm64-msvc@1.32.0: + optional: true + + lightningcss-win32-x64-msvc@1.32.0: + optional: true + + lightningcss@1.32.0: + dependencies: + detect-libc: 2.1.2 + optionalDependencies: + lightningcss-android-arm64: 1.32.0 + lightningcss-darwin-arm64: 1.32.0 + lightningcss-darwin-x64: 1.32.0 + lightningcss-freebsd-x64: 1.32.0 + lightningcss-linux-arm-gnueabihf: 1.32.0 + lightningcss-linux-arm64-gnu: 1.32.0 + lightningcss-linux-arm64-musl: 1.32.0 + lightningcss-linux-x64-gnu: 1.32.0 + lightningcss-linux-x64-musl: 1.32.0 + lightningcss-win32-arm64-msvc: 1.32.0 + lightningcss-win32-x64-msvc: 1.32.0 + + locate-path@6.0.0: + dependencies: + p-locate: 5.0.0 + + lodash@4.18.1: {} + + lru-cache@11.3.6: {} + + lru-cache@5.1.1: + dependencies: + yallist: 3.1.1 + + lz-string@1.5.0: {} + + magic-string@0.30.21: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + + math-intrinsics@1.1.0: {} + + mdn-data@2.27.1: {} + + mime-db@1.52.0: {} + + mime-types@2.1.35: + dependencies: + mime-db: 1.52.0 + + min-indent@1.0.1: {} + + minimatch@10.2.5: + dependencies: + brace-expansion: 5.0.6 + + ms@2.1.3: {} + + msw@2.14.5(@types/node@24.12.3)(typescript@6.0.3): + dependencies: + '@inquirer/confirm': 6.0.13(@types/node@24.12.3) + '@mswjs/interceptors': 0.41.8 + '@open-draft/deferred-promise': 3.0.0 + '@types/statuses': 2.0.6 + cookie: 1.1.1 + graphql: 16.14.0 + headers-polyfill: 5.0.1 + is-node-process: 1.2.0 + outvariant: 1.4.3 + path-to-regexp: 6.3.0 + picocolors: 1.1.1 + rettime: 0.11.11 + statuses: 2.0.2 + strict-event-emitter: 0.5.1 + tough-cookie: 6.0.1 + type-fest: 5.6.0 + until-async: 3.0.2 + yargs: 17.7.2 + optionalDependencies: + typescript: 6.0.3 + transitivePeerDependencies: + - '@types/node' + + mute-stream@3.0.0: {} + + nano-memoize@3.0.16: {} + + nanoid@3.3.12: {} + + natural-compare@1.4.0: {} + + node-releases@2.0.38: {} + + obug@2.1.1: {} + + optionator@0.9.4: + dependencies: + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + word-wrap: 1.2.5 + + outvariant@1.4.3: {} + + p-limit@3.1.0: + dependencies: + yocto-queue: 0.1.0 + + p-locate@5.0.0: + dependencies: + p-limit: 3.1.0 + + parse5@8.0.1: + dependencies: + entities: 8.0.0 + + path-exists@4.0.0: {} + + path-key@3.1.1: {} + + path-to-regexp@6.3.0: {} + + pathe@2.0.3: {} + + picocolors@1.1.1: {} + + picomatch@4.0.4: {} + + postcss@8.5.14: + dependencies: + nanoid: 3.3.12 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + prelude-ls@1.2.1: {} + + pretty-format@27.5.1: + dependencies: + ansi-regex: 5.0.1 + ansi-styles: 5.2.0 + react-is: 17.0.2 + + proxy-from-env@2.1.0: {} + + punycode@2.3.1: {} + + rc-field-form@1.44.0(react-dom@19.2.6(react@19.2.6))(react@19.2.6): + dependencies: + '@babel/runtime': 7.29.2 + async-validator: 4.2.5 + rc-util: 5.44.4(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + react: 19.2.6 + react-dom: 19.2.6(react@19.2.6) + + rc-motion@2.9.5(react-dom@19.2.6(react@19.2.6))(react@19.2.6): + dependencies: + '@babel/runtime': 7.29.2 + classnames: 2.5.1 + rc-util: 5.44.4(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + react: 19.2.6 + react-dom: 19.2.6(react@19.2.6) + + rc-segmented@2.4.1(react-dom@19.2.6(react@19.2.6))(react@19.2.6): + dependencies: + '@babel/runtime': 7.29.2 + classnames: 2.5.1 + rc-motion: 2.9.5(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + rc-util: 5.44.4(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + react: 19.2.6 + react-dom: 19.2.6(react@19.2.6) + + rc-util@5.44.4(react-dom@19.2.6(react@19.2.6))(react@19.2.6): + dependencies: + '@babel/runtime': 7.29.2 + react: 19.2.6 + react-dom: 19.2.6(react@19.2.6) + react-is: 18.3.1 + + react-dom@19.2.6(react@19.2.6): + dependencies: + react: 19.2.6 + scheduler: 0.27.0 + + react-fast-compare@3.2.2: {} + + react-is@17.0.2: {} + + react-is@18.3.1: {} + + react-router-dom@7.15.0(react-dom@19.2.6(react@19.2.6))(react@19.2.6): + dependencies: + react: 19.2.6 + react-dom: 19.2.6(react@19.2.6) + react-router: 7.15.0(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + + react-router@7.15.0(react-dom@19.2.6(react@19.2.6))(react@19.2.6): + dependencies: + cookie: 1.1.1 + react: 19.2.6 + set-cookie-parser: 2.7.2 + optionalDependencies: + react-dom: 19.2.6(react@19.2.6) + + react@19.2.6: {} + + redent@3.0.0: + dependencies: + indent-string: 4.0.0 + strip-indent: 3.0.0 + + require-directory@2.1.1: {} + + require-from-string@2.0.2: {} + + resize-observer-polyfill@1.5.1: {} + + rettime@0.11.11: {} + + rolldown@1.0.0-rc.18: + dependencies: + '@oxc-project/types': 0.128.0 + '@rolldown/pluginutils': 1.0.0-rc.18 + optionalDependencies: + '@rolldown/binding-android-arm64': 1.0.0-rc.18 + '@rolldown/binding-darwin-arm64': 1.0.0-rc.18 + '@rolldown/binding-darwin-x64': 1.0.0-rc.18 + '@rolldown/binding-freebsd-x64': 1.0.0-rc.18 + '@rolldown/binding-linux-arm-gnueabihf': 1.0.0-rc.18 + '@rolldown/binding-linux-arm64-gnu': 1.0.0-rc.18 + '@rolldown/binding-linux-arm64-musl': 1.0.0-rc.18 + '@rolldown/binding-linux-ppc64-gnu': 1.0.0-rc.18 + '@rolldown/binding-linux-s390x-gnu': 1.0.0-rc.18 + '@rolldown/binding-linux-x64-gnu': 1.0.0-rc.18 + '@rolldown/binding-linux-x64-musl': 1.0.0-rc.18 + '@rolldown/binding-openharmony-arm64': 1.0.0-rc.18 + '@rolldown/binding-wasm32-wasi': 1.0.0-rc.18 + '@rolldown/binding-win32-arm64-msvc': 1.0.0-rc.18 + '@rolldown/binding-win32-x64-msvc': 1.0.0-rc.18 + + runes2@1.1.4: {} + + saxes@6.0.0: + dependencies: + xmlchars: 2.2.0 + + scheduler@0.27.0: {} + + screenfull@5.2.0: {} + + semver@6.3.1: {} + + semver@7.8.0: {} + + set-cookie-parser@2.7.2: {} + + set-cookie-parser@3.1.0: {} + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + + siginfo@2.0.0: {} + + signal-exit@4.1.0: {} + + source-map-js@1.2.1: {} + + stackback@0.0.2: {} + + staged-components@1.1.3(react@19.2.6): + dependencies: + react: 19.2.6 + + statuses@2.0.2: {} + + std-env@4.1.0: {} + + strict-event-emitter@0.5.1: {} + + string-width@4.2.3: + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + + strip-indent@3.0.0: + dependencies: + min-indent: 1.0.1 + + symbol-tree@3.2.4: {} + + tagged-tag@1.0.0: {} + + tinybench@2.9.0: {} + + tinyexec@1.1.2: {} + + tinyglobby@0.2.16: + dependencies: + fdir: 6.5.0(picomatch@4.0.4) + picomatch: 4.0.4 + + tinyrainbow@3.1.0: {} + + tldts-core@7.0.30: {} + + tldts@7.0.30: + dependencies: + tldts-core: 7.0.30 + + tough-cookie@6.0.1: + dependencies: + tldts: 7.0.30 + + tr46@6.0.0: + dependencies: + punycode: 2.3.1 + + ts-api-utils@2.5.0(typescript@6.0.3): + dependencies: + typescript: 6.0.3 + + tslib@2.3.0: {} + + tslib@2.8.1: {} + + type-check@0.4.0: + dependencies: + prelude-ls: 1.2.1 + + type-fest@5.6.0: + dependencies: + tagged-tag: 1.0.0 + + typescript-eslint@8.59.2(eslint@10.3.0)(typescript@6.0.3): + dependencies: + '@typescript-eslint/eslint-plugin': 8.59.2(@typescript-eslint/parser@8.59.2(eslint@10.3.0)(typescript@6.0.3))(eslint@10.3.0)(typescript@6.0.3) + '@typescript-eslint/parser': 8.59.2(eslint@10.3.0)(typescript@6.0.3) + '@typescript-eslint/typescript-estree': 8.59.2(typescript@6.0.3) + '@typescript-eslint/utils': 8.59.2(eslint@10.3.0)(typescript@6.0.3) + eslint: 10.3.0 + typescript: 6.0.3 + transitivePeerDependencies: + - supports-color + + typescript@6.0.3: {} + + undici-types@7.16.0: {} + + undici@7.25.0: {} + + until-async@3.0.2: {} + + update-browserslist-db@1.2.3(browserslist@4.28.2): + dependencies: + browserslist: 4.28.2 + escalade: 3.2.0 + picocolors: 1.1.1 + + uri-js@4.4.1: + dependencies: + punycode: 2.3.1 + + use-sync-external-store@1.6.0(react@19.2.6): + dependencies: + react: 19.2.6 + + vite@8.0.11(@types/node@24.12.3): + dependencies: + lightningcss: 1.32.0 + picomatch: 4.0.4 + postcss: 8.5.14 + rolldown: 1.0.0-rc.18 + tinyglobby: 0.2.16 + optionalDependencies: + '@types/node': 24.12.3 + fsevents: 2.3.3 + + vitest@4.1.5(@types/node@24.12.3)(jsdom@29.1.1)(msw@2.14.5(@types/node@24.12.3)(typescript@6.0.3))(vite@8.0.11(@types/node@24.12.3)): + dependencies: + '@vitest/expect': 4.1.5 + '@vitest/mocker': 4.1.5(msw@2.14.5(@types/node@24.12.3)(typescript@6.0.3))(vite@8.0.11(@types/node@24.12.3)) + '@vitest/pretty-format': 4.1.5 + '@vitest/runner': 4.1.5 + '@vitest/snapshot': 4.1.5 + '@vitest/spy': 4.1.5 + '@vitest/utils': 4.1.5 + es-module-lexer: 2.1.0 + expect-type: 1.3.0 + magic-string: 0.30.21 + obug: 2.1.1 + pathe: 2.0.3 + picomatch: 4.0.4 + std-env: 4.1.0 + tinybench: 2.9.0 + tinyexec: 1.1.2 + tinyglobby: 0.2.16 + tinyrainbow: 3.1.0 + vite: 8.0.11(@types/node@24.12.3) + why-is-node-running: 2.3.0 + optionalDependencies: + '@types/node': 24.12.3 + jsdom: 29.1.1 + transitivePeerDependencies: + - msw + + w3c-xmlserializer@5.0.0: + dependencies: + xml-name-validator: 5.0.0 + + webidl-conversions@8.0.1: {} + + whatwg-mimetype@5.0.0: {} + + whatwg-url@16.0.1: + dependencies: + '@exodus/bytes': 1.15.0 + tr46: 6.0.0 + webidl-conversions: 8.0.1 + transitivePeerDependencies: + - '@noble/hashes' + + which@2.0.2: + dependencies: + isexe: 2.0.0 + + why-is-node-running@2.3.0: + dependencies: + siginfo: 2.0.0 + stackback: 0.0.2 + + word-wrap@1.2.5: {} + + wrap-ansi@7.0.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + xml-name-validator@5.0.0: {} + + xmlchars@2.2.0: {} + + y18n@5.0.8: {} + + yallist@3.1.1: {} + + yargs-parser@21.1.1: {} + + yargs@17.7.2: + dependencies: + cliui: 8.0.1 + escalade: 3.2.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 21.1.1 + + yocto-queue@0.1.0: {} + + zod-validation-error@4.0.2(zod@4.4.3): + dependencies: + zod: 4.4.3 + + zod@4.4.3: {} + + zrender@6.0.0: + dependencies: + tslib: 2.3.0 + + zustand@5.0.13(@types/react@19.2.14)(react@19.2.6)(use-sync-external-store@1.6.0(react@19.2.6)): + optionalDependencies: + '@types/react': 19.2.14 + react: 19.2.6 + use-sync-external-store: 1.6.0(react@19.2.6) diff --git a/dashboard-frontend/public/favicon.svg b/dashboard-frontend/public/favicon.svg new file mode 100644 index 0000000..6893eb1 --- /dev/null +++ b/dashboard-frontend/public/favicon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/dashboard-frontend/public/icons.svg b/dashboard-frontend/public/icons.svg new file mode 100644 index 0000000..e952219 --- /dev/null +++ b/dashboard-frontend/public/icons.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dashboard-frontend/public/mockServiceWorker.js b/dashboard-frontend/public/mockServiceWorker.js new file mode 100644 index 0000000..9cd8401 --- /dev/null +++ b/dashboard-frontend/public/mockServiceWorker.js @@ -0,0 +1,349 @@ +/* eslint-disable */ +/* tslint:disable */ + +/** + * Mock Service Worker. + * @see https://github.com/mswjs/msw + * - Please do NOT modify this file. + */ + +const PACKAGE_VERSION = '2.14.5' +const INTEGRITY_CHECKSUM = '4db4a41e972cec1b64cc569c66952d82' +const IS_MOCKED_RESPONSE = Symbol('isMockedResponse') +const activeClientIds = new Set() + +addEventListener('install', function () { + self.skipWaiting() +}) + +addEventListener('activate', function (event) { + event.waitUntil(self.clients.claim()) +}) + +addEventListener('message', async function (event) { + const clientId = Reflect.get(event.source || {}, 'id') + + if (!clientId || !self.clients) { + return + } + + const client = await self.clients.get(clientId) + + if (!client) { + return + } + + const allClients = await self.clients.matchAll({ + type: 'window', + }) + + switch (event.data) { + case 'KEEPALIVE_REQUEST': { + sendToClient(client, { + type: 'KEEPALIVE_RESPONSE', + }) + break + } + + case 'INTEGRITY_CHECK_REQUEST': { + sendToClient(client, { + type: 'INTEGRITY_CHECK_RESPONSE', + payload: { + packageVersion: PACKAGE_VERSION, + checksum: INTEGRITY_CHECKSUM, + }, + }) + break + } + + case 'MOCK_ACTIVATE': { + activeClientIds.add(clientId) + + sendToClient(client, { + type: 'MOCKING_ENABLED', + payload: { + client: { + id: client.id, + frameType: client.frameType, + }, + }, + }) + break + } + + case 'CLIENT_CLOSED': { + activeClientIds.delete(clientId) + + const remainingClients = allClients.filter((client) => { + return client.id !== clientId + }) + + // Unregister itself when there are no more clients + if (remainingClients.length === 0) { + self.registration.unregister() + } + + break + } + } +}) + +addEventListener('fetch', function (event) { + const requestInterceptedAt = Date.now() + + // Bypass navigation requests. + if (event.request.mode === 'navigate') { + return + } + + // Opening the DevTools triggers the "only-if-cached" request + // that cannot be handled by the worker. Bypass such requests. + if ( + event.request.cache === 'only-if-cached' && + event.request.mode !== 'same-origin' + ) { + return + } + + // Bypass all requests when there are no active clients. + // Prevents the self-unregistered worked from handling requests + // after it's been terminated (still remains active until the next reload). + if (activeClientIds.size === 0) { + return + } + + const requestId = crypto.randomUUID() + event.respondWith(handleRequest(event, requestId, requestInterceptedAt)) +}) + +/** + * @param {FetchEvent} event + * @param {string} requestId + * @param {number} requestInterceptedAt + */ +async function handleRequest(event, requestId, requestInterceptedAt) { + const client = await resolveMainClient(event) + const requestCloneForEvents = event.request.clone() + const response = await getResponse( + event, + client, + requestId, + requestInterceptedAt, + ) + + // Send back the response clone for the "response:*" life-cycle events. + // Ensure MSW is active and ready to handle the message, otherwise + // this message will pend indefinitely. + if (client && activeClientIds.has(client.id)) { + const serializedRequest = await serializeRequest(requestCloneForEvents) + + // Clone the response so both the client and the library could consume it. + const responseClone = response.clone() + + sendToClient( + client, + { + type: 'RESPONSE', + payload: { + isMockedResponse: IS_MOCKED_RESPONSE in response, + request: { + id: requestId, + ...serializedRequest, + }, + response: { + type: responseClone.type, + status: responseClone.status, + statusText: responseClone.statusText, + headers: Object.fromEntries(responseClone.headers.entries()), + body: responseClone.body, + }, + }, + }, + responseClone.body ? [serializedRequest.body, responseClone.body] : [], + ) + } + + return response +} + +/** + * Resolve the main client for the given event. + * Client that issues a request doesn't necessarily equal the client + * that registered the worker. It's with the latter the worker should + * communicate with during the response resolving phase. + * @param {FetchEvent} event + * @returns {Promise} + */ +async function resolveMainClient(event) { + const client = await self.clients.get(event.clientId) + + if (activeClientIds.has(event.clientId)) { + return client + } + + if (client?.frameType === 'top-level') { + return client + } + + const allClients = await self.clients.matchAll({ + type: 'window', + }) + + return allClients + .filter((client) => { + // Get only those clients that are currently visible. + return client.visibilityState === 'visible' + }) + .find((client) => { + // Find the client ID that's recorded in the + // set of clients that have registered the worker. + return activeClientIds.has(client.id) + }) +} + +/** + * @param {FetchEvent} event + * @param {Client | undefined} client + * @param {string} requestId + * @param {number} requestInterceptedAt + * @returns {Promise} + */ +async function getResponse(event, client, requestId, requestInterceptedAt) { + // Clone the request because it might've been already used + // (i.e. its body has been read and sent to the client). + const requestClone = event.request.clone() + + function passthrough() { + // Cast the request headers to a new Headers instance + // so the headers can be manipulated with. + const headers = new Headers(requestClone.headers) + + // Remove the "accept" header value that marked this request as passthrough. + // This prevents request alteration and also keeps it compliant with the + // user-defined CORS policies. + const acceptHeader = headers.get('accept') + if (acceptHeader) { + const values = acceptHeader.split(',').map((value) => value.trim()) + const filteredValues = values.filter( + (value) => value !== 'msw/passthrough', + ) + + if (filteredValues.length > 0) { + headers.set('accept', filteredValues.join(', ')) + } else { + headers.delete('accept') + } + } + + return fetch(requestClone, { headers }) + } + + // Bypass mocking when the client is not active. + if (!client) { + return passthrough() + } + + // Bypass initial page load requests (i.e. static assets). + // The absence of the immediate/parent client in the map of the active clients + // means that MSW hasn't dispatched the "MOCK_ACTIVATE" event yet + // and is not ready to handle requests. + if (!activeClientIds.has(client.id)) { + return passthrough() + } + + // Notify the client that a request has been intercepted. + const serializedRequest = await serializeRequest(event.request) + const clientMessage = await sendToClient( + client, + { + type: 'REQUEST', + payload: { + id: requestId, + interceptedAt: requestInterceptedAt, + ...serializedRequest, + }, + }, + [serializedRequest.body], + ) + + switch (clientMessage.type) { + case 'MOCK_RESPONSE': { + return respondWithMock(clientMessage.data) + } + + case 'PASSTHROUGH': { + return passthrough() + } + } + + return passthrough() +} + +/** + * @param {Client} client + * @param {any} message + * @param {Array} transferrables + * @returns {Promise} + */ +function sendToClient(client, message, transferrables = []) { + return new Promise((resolve, reject) => { + const channel = new MessageChannel() + + channel.port1.onmessage = (event) => { + if (event.data && event.data.error) { + return reject(event.data.error) + } + + resolve(event.data) + } + + client.postMessage(message, [ + channel.port2, + ...transferrables.filter(Boolean), + ]) + }) +} + +/** + * @param {Response} response + * @returns {Response} + */ +function respondWithMock(response) { + // Setting response status code to 0 is a no-op. + // However, when responding with a "Response.error()", the produced Response + // instance will have status code set to 0. Since it's not possible to create + // a Response instance with status code 0, handle that use-case separately. + if (response.status === 0) { + return Response.error() + } + + const mockedResponse = new Response(response.body, response) + + Reflect.defineProperty(mockedResponse, IS_MOCKED_RESPONSE, { + value: true, + enumerable: true, + }) + + return mockedResponse +} + +/** + * @param {Request} request + */ +async function serializeRequest(request) { + return { + url: request.url, + mode: request.mode, + method: request.method, + headers: Object.fromEntries(request.headers.entries()), + cache: request.cache, + credentials: request.credentials, + destination: request.destination, + integrity: request.integrity, + redirect: request.redirect, + referrer: request.referrer, + referrerPolicy: request.referrerPolicy, + body: await request.arrayBuffer(), + keepalive: request.keepalive, + } +} diff --git a/dashboard-frontend/src/App.tsx b/dashboard-frontend/src/App.tsx new file mode 100644 index 0000000..5f82b63 --- /dev/null +++ b/dashboard-frontend/src/App.tsx @@ -0,0 +1,27 @@ +import { BrowserRouter, Navigate, Route, Routes } from 'react-router-dom' +import { AppProviders } from './app/providers/AppProviders' +import { MobileLayout } from './app/layouts/MobileLayout' +import { BossDashboardPage } from './features/boss-dashboard/pages/BossDashboardPage' +import { DailyReportPage, ProfilePage, RiskCenterPage, TodaySnapshotPage } from './features/boss-dashboard/pages/OperationsPages' + +function App() { + return ( + + + + } /> + }> + } /> + } /> + } /> + } /> + } /> + + } /> + + + + ) +} + +export default App diff --git a/dashboard-frontend/src/app/layouts/MobileLayout.tsx b/dashboard-frontend/src/app/layouts/MobileLayout.tsx new file mode 100644 index 0000000..1d18642 --- /dev/null +++ b/dashboard-frontend/src/app/layouts/MobileLayout.tsx @@ -0,0 +1,34 @@ +import { AppOutline, BellOutline, FileOutline, HistogramOutline, UserOutline } from 'antd-mobile-icons' +import { Outlet, useLocation, useNavigate } from 'react-router-dom' +import { SafeArea, TabBar } from 'antd-mobile' + +const tabs = [ + { key: '/h5/dashboard/boss', title: '首页', icon: }, + { key: '/h5/dashboard/daily-report', title: '日报', icon: }, + { key: '/h5/dashboard/today-snapshot', title: '快报', icon: }, + { key: '/h5/dashboard/risk-center', title: '风险', icon: }, + { key: '/h5/dashboard/profile', title: '我的', icon: }, +] + +export function MobileLayout() { + const location = useLocation() + const navigate = useNavigate() + + const activeKey = tabs.find((tab) => location.pathname.startsWith(tab.key))?.key ?? tabs[0].key + + return ( +
+
+ +
+ +
+ ) +} diff --git a/dashboard-frontend/src/app/providers/AppProviders.tsx b/dashboard-frontend/src/app/providers/AppProviders.tsx new file mode 100644 index 0000000..5eda6c6 --- /dev/null +++ b/dashboard-frontend/src/app/providers/AppProviders.tsx @@ -0,0 +1,26 @@ +import { ConfigProvider } from 'antd-mobile' +import zhCN from 'antd-mobile/es/locales/zh-CN' +import { QueryClient, QueryClientProvider } from '@tanstack/react-query' +import type { ReactNode } from 'react' + +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + staleTime: 60 * 1000, + retry: 1, + refetchOnWindowFocus: false, + }, + }, +}) + +type AppProvidersProps = { + children: ReactNode +} + +export function AppProviders({ children }: AppProvidersProps) { + return ( + + {children} + + ) +} diff --git a/dashboard-frontend/src/components/charts/MiniTrendChart.tsx b/dashboard-frontend/src/components/charts/MiniTrendChart.tsx new file mode 100644 index 0000000..147a64b --- /dev/null +++ b/dashboard-frontend/src/components/charts/MiniTrendChart.tsx @@ -0,0 +1,76 @@ +import * as echarts from 'echarts/core' +import { GridComponent, TooltipComponent } from 'echarts/components' +import { BarChart, LineChart } from 'echarts/charts' +import { CanvasRenderer } from 'echarts/renderers' +import { useEffect, useMemo, useRef } from 'react' +import type { TrendPoint } from '../../features/boss-dashboard/types' + +echarts.use([GridComponent, TooltipComponent, LineChart, BarChart, CanvasRenderer]) + +type MiniTrendChartProps = { + data: TrendPoint[] +} + +export function MiniTrendChart({ data }: MiniTrendChartProps) { + const chartRef = useRef(null) + + const option = useMemo( + () => ({ + color: ['#ff5b36', '#ffb000'], + grid: { left: 8, right: 8, top: 24, bottom: 18, containLabel: true }, + tooltip: { + trigger: 'axis', + confine: true, + valueFormatter: (value: number | string) => Number(value).toLocaleString('zh-CN'), + }, + xAxis: { + type: 'category', + data: data.map((item) => item.date), + axisLine: { show: false }, + axisTick: { show: false }, + }, + yAxis: [ + { type: 'value', show: false }, + { type: 'value', show: false }, + ], + series: [ + { + name: '成交额', + type: 'line', + smooth: true, + yAxisIndex: 0, + data: data.map((item) => item.amount), + symbol: 'circle', + symbolSize: 5, + lineStyle: { width: 3 }, + areaStyle: { opacity: 0.08 }, + }, + { + name: '订单数', + type: 'bar', + yAxisIndex: 1, + data: data.map((item) => item.orders), + barWidth: 8, + borderRadius: 6, + }, + ], + }), + [data], + ) + + useEffect(() => { + if (!chartRef.current) return undefined + const chart = echarts.init(chartRef.current) + chart.setOption(option) + + const resize = () => chart.resize() + window.addEventListener('resize', resize) + + return () => { + window.removeEventListener('resize', resize) + chart.dispose() + } + }, [option]) + + return
+} diff --git a/dashboard-frontend/src/components/kpi/KpiCard.tsx b/dashboard-frontend/src/components/kpi/KpiCard.tsx new file mode 100644 index 0000000..241bd4b --- /dev/null +++ b/dashboard-frontend/src/components/kpi/KpiCard.tsx @@ -0,0 +1,32 @@ +import { Skeleton } from 'antd-mobile' +import { formatMetricValue, formatTrend } from '../../utils/format' +import type { KpiMetric } from '../../features/boss-dashboard/types' + +type KpiCardProps = { + metric: KpiMetric + loading?: boolean +} + +export function KpiCard({ metric, loading }: KpiCardProps) { + if (loading) { + return ( +
+ + +
+ ) + } + + return ( +
+

{metric.title}

+ {formatMetricValue(metric.value, metric.unit)} + {(metric.trendLabel || metric.trendValue !== undefined) && ( +

+ {metric.trendLabel} + {metric.trendValue !== undefined && {formatTrend(metric.trendValue)}} +

+ )} +
+ ) +} diff --git a/dashboard-frontend/src/features/boss-dashboard/api.ts b/dashboard-frontend/src/features/boss-dashboard/api.ts new file mode 100644 index 0000000..c3c49f2 --- /dev/null +++ b/dashboard-frontend/src/features/boss-dashboard/api.ts @@ -0,0 +1,18 @@ +import { useQuery } from '@tanstack/react-query' +import { getApiData, getBlob } from '../../services/http/client' +import type { DashboardOverview } from './types' + +export const dashboardQueryKeys = { + overview: (date?: string) => ['dashboard', 'overview', date ?? 'default'] as const, +} + +export function useDashboardOverview(date?: string) { + return useQuery({ + queryKey: dashboardQueryKeys.overview(date), + queryFn: () => getApiData(date ? `/dashboard/overview?date=${date}` : '/dashboard/overview'), + }) +} + +export function downloadDailyReportArchive(date?: string) { + return getBlob(date ? `/dashboard/daily-report/archive?date=${date}` : '/dashboard/daily-report/archive') +} diff --git a/dashboard-frontend/src/features/boss-dashboard/components/RankList.tsx b/dashboard-frontend/src/features/boss-dashboard/components/RankList.tsx new file mode 100644 index 0000000..b629948 --- /dev/null +++ b/dashboard-frontend/src/features/boss-dashboard/components/RankList.tsx @@ -0,0 +1,38 @@ +import { RightOutline } from 'antd-mobile-icons' +import { formatMoney, formatNumber } from '../../../utils/format' +import type { RankItem } from '../types' + +type RankListProps = { + title: string + items: RankItem[] + valueType?: 'money' | 'number' +} + +export function RankList({ title, items, valueType = 'money' }: RankListProps) { + return ( +
+
+
+

Top 3

+

{title}

+
+ +
+
+ {items.map((item, index) => ( + + ))} +
+
+ ) +} diff --git a/dashboard-frontend/src/features/boss-dashboard/components/RiskAlertSection.tsx b/dashboard-frontend/src/features/boss-dashboard/components/RiskAlertSection.tsx new file mode 100644 index 0000000..188fbf9 --- /dev/null +++ b/dashboard-frontend/src/features/boss-dashboard/components/RiskAlertSection.tsx @@ -0,0 +1,42 @@ +import { Tag } from 'antd-mobile' +import type { RiskAlert, RiskLevel } from '../types' + +type RiskAlertSectionProps = { + risks: RiskAlert[] +} + +const levelMeta: Record = { + red: { color: 'danger', label: '红色' }, + yellow: { color: 'warning', label: '黄色' }, + gray: { color: 'default', label: '灰色' }, +} + +export function RiskAlertSection({ risks }: RiskAlertSectionProps) { + return ( +
+
+
+

Risk

+

风险预警

+
+ {risks.length} 条 +
+
+ {risks.map((risk) => { + const meta = levelMeta[risk.level] + return ( + + ) + })} +
+
+ ) +} diff --git a/dashboard-frontend/src/features/boss-dashboard/components/TodaySnapshotSection.tsx b/dashboard-frontend/src/features/boss-dashboard/components/TodaySnapshotSection.tsx new file mode 100644 index 0000000..db7f508 --- /dev/null +++ b/dashboard-frontend/src/features/boss-dashboard/components/TodaySnapshotSection.tsx @@ -0,0 +1,64 @@ +import { CapsuleTabs, Tag } from 'antd-mobile' +import { useState } from 'react' +import { formatMoney, formatNumber } from '../../../utils/format' +import type { SnapshotSlot, TodaySnapshot } from '../types' + +type TodaySnapshotSectionProps = { + snapshots: TodaySnapshot[] +} + +const statusMap = { + pending: { color: 'default', label: '待生成' }, + success: { color: 'success', label: '已生成' }, + failed: { color: 'danger', label: '生成失败' }, + temporary: { color: 'warning', label: '临时数据' }, +} as const + +export function TodaySnapshotSection({ snapshots }: TodaySnapshotSectionProps) { + const [activeSlot, setActiveSlot] = useState('1015') + const activeSnapshot = snapshots.find((snapshot) => snapshot.slot === activeSlot) ?? snapshots[0] + const status = statusMap[activeSnapshot.status] + + return ( +
+
+
+

今日节点

+

抢购 / 寄卖快报

+
+ {status.label} +
+ + setActiveSlot(key as SnapshotSlot)}> + {snapshots.map((snapshot) => ( + + ))} + + +
+

{activeSnapshot.message}

+ {activeSnapshot.generatedAt &&

生成时间:{activeSnapshot.generatedAt}

} +
+ + 采购用户{formatNumber(activeSnapshot.purchaseUsers)}人 + + + 订单数{formatNumber(activeSnapshot.orderCount)}单 + + + 成交额{formatMoney(activeSnapshot.dealAmount)} + + + 支付额{formatMoney(activeSnapshot.paidAmount)} + + + 新增商品{formatNumber(activeSnapshot.newMerchandiseCount)}件 + + + 奖金变化{formatMoney(Number(activeSnapshot.selfBonusChange) + Number(activeSnapshot.shareBonusChange))} + +
+
+
+ ) +} diff --git a/dashboard-frontend/src/features/boss-dashboard/mock.ts b/dashboard-frontend/src/features/boss-dashboard/mock.ts new file mode 100644 index 0000000..6fc7d4f --- /dev/null +++ b/dashboard-frontend/src/features/boss-dashboard/mock.ts @@ -0,0 +1,104 @@ +import type { DashboardOverview } from './types' + +export const dashboardMock: DashboardOverview = { + businessDate: '2026-05-10', + generatedAt: '2026-05-11 00:10:12', + summary: '昨日成交保持稳定,采购用户略有增长;资金风险主要集中在大额待提现和积分比例异常。', + kpis: [ + { key: 'dealAmount', title: '昨日成交额', value: 1289360.42, unit: '元', trendLabel: '较前日', trendValue: 8.6, status: 'success', featured: true }, + { key: 'orderCount', title: '昨日订单数', value: 1842, unit: '单', trendLabel: '较前日', trendValue: 4.1, status: 'success' }, + { key: 'purchaseUsers', title: '采购用户', value: 936, unit: '人', trendLabel: '较前日', trendValue: 2.7, status: 'success' }, + { key: 'newUsers', title: '新增用户', value: 318, unit: '人', trendLabel: '较前日', trendValue: -3.2, status: 'warning' }, + { key: 'newMerchandise', title: '新增寄售商品', value: 472, unit: '件', trendLabel: '较前日', trendValue: 12.4, status: 'success' }, + { key: 'selfBonus', title: '个人奖金发放', value: 168230.36, unit: '元', trendLabel: '较前日', trendValue: 6.8, status: 'normal' }, + { key: 'shareBonus', title: '推广奖金发放', value: 82460.18, unit: '元', trendLabel: '较前日', trendValue: 1.9, status: 'normal' }, + { key: 'pendingAmount', title: '待支付/待结算', value: 95620.11, unit: '元', trendLabel: '需关注', status: 'warning' }, + ], + fundPool: [ + { key: 'balance', title: '余额总额', value: 728903.22, unit: '元', status: 'normal' }, + { key: 'coupon', title: '优惠券总额', value: 391082.88, unit: '元', status: 'normal' }, + { key: 'selfBonusPool', title: '个人奖金总额', value: 836942.14, unit: '元', status: 'warning' }, + { key: 'shareBonusPool', title: '推广奖金总额', value: 295402.77, unit: '元', status: 'normal' }, + { key: 'integral', title: '积分总额', value: 418471.07, unit: '分', status: 'normal' }, + { key: 'withdrawPending', title: '待审核提现', value: 63200, unit: '元', status: 'danger' }, + ], + snapshots: [ + { + slot: '1015', + title: '10:15 上午快报', + status: 'success', + generatedAt: '2026-05-11 10:15:08', + message: '上午抢购节点已完成,上一日寄卖商品消化情况良好,采购用户和成交额略高于昨日同节点。', + purchaseUsers: 421, + orderCount: 756, + dealAmount: 526880.2, + paidAmount: 498320.5, + newMerchandiseCount: 185, + selfBonusChange: 64230.3, + shareBonusChange: 31820.1, + }, + { + slot: '1455', + title: '14:55 下午快报', + status: 'pending', + message: '下午寄卖/转卖节点尚未生成,预计 14:55 后可查看用户抢购商品的再次上架情况。', + purchaseUsers: 0, + orderCount: 0, + dealAmount: 0, + paidAmount: 0, + newMerchandiseCount: 0, + selfBonusChange: 0, + shareBonusChange: 0, + }, + ], + trends: [ + { date: '05-04', amount: 948000, orders: 1390, newUsers: 226, bonus: 186000 }, + { date: '05-05', amount: 1024000, orders: 1512, newUsers: 251, bonus: 194000 }, + { date: '05-06', amount: 1119000, orders: 1604, newUsers: 287, bonus: 205000 }, + { date: '05-07', amount: 1086000, orders: 1542, newUsers: 243, bonus: 198000 }, + { date: '05-08', amount: 1198000, orders: 1731, newUsers: 302, bonus: 221000 }, + { date: '05-09', amount: 1187200, orders: 1769, newUsers: 329, bonus: 229000 }, + { date: '05-10', amount: 1289360, orders: 1842, newUsers: 318, bonus: 250690 }, + ], + userRanks: [ + { id: 'u1', name: '刘先生', value: 96520, description: '个人奖金 + 推广奖金 + 积分折算', badge: '高价值' }, + { id: 'u2', name: '陈女士', value: 81230, description: '昨日采购 12 单', badge: '活跃' }, + { id: 'u3', name: '周先生', value: 75880, description: '团队新增 18 人' }, + ], + teamRanks: [ + { id: 't1', name: '华东一队', value: 386200, description: '成交额第一,团队收益 4.8 万', badge: 'TOP1' }, + { id: 't2', name: '苏州团队', value: 318760, description: '采购用户 182 人' }, + { id: 't3', name: '扬州团队', value: 287500, description: '新增成员 36 人' }, + ], + productRanks: [ + { id: 'p1', name: '高端礼盒 A 款', value: 128800, description: '上架 7 天未成交', badge: '滞销' }, + { id: 'p2', name: '精选组合 B 款', value: 98600, description: '高货值待成交' }, + { id: 'p3', name: '会员专享 C 款', value: 83500, description: '浏览高,成交低' }, + ], + risks: [ + { + id: 'r1', + level: 'red', + type: '资金', + title: '大额待审核提现', + description: '当前待审核提现 6.32 万,建议今日处理。', + discoveredAt: '11:00', + }, + { + id: 'r2', + level: 'yellow', + type: '积分', + title: '积分与个人奖金比例异常', + description: '发现 3 名用户积分未接近个人奖金的 1/2。', + discoveredAt: '10:40', + }, + { + id: 'r3', + level: 'gray', + type: '数据', + title: '用户资料不一致', + description: 'wa_users 与 eb_user 有 5 条手机号不一致。', + discoveredAt: '09:55', + }, + ], +} diff --git a/dashboard-frontend/src/features/boss-dashboard/pages/BossDashboardPage.tsx b/dashboard-frontend/src/features/boss-dashboard/pages/BossDashboardPage.tsx new file mode 100644 index 0000000..e6c6fde --- /dev/null +++ b/dashboard-frontend/src/features/boss-dashboard/pages/BossDashboardPage.tsx @@ -0,0 +1,105 @@ +import { Button, DotLoading, ErrorBlock } from 'antd-mobile' +import { KpiCard } from '../../../components/kpi/KpiCard' +import { MiniTrendChart } from '../../../components/charts/MiniTrendChart' +import { formatMoney } from '../../../utils/format' +import { useDashboardOverview } from '../api' +import { RankList } from '../components/RankList' +import { RiskAlertSection } from '../components/RiskAlertSection' +import { TodaySnapshotSection } from '../components/TodaySnapshotSection' + +export function BossDashboardPage() { + const { data, isLoading, isError, refetch } = useDashboardOverview() + + if (isLoading) { + return ( +
+ +

正在生成经营简报...

+
+ ) + } + + if (isError || !data) { + return ( +
+ + +
+ ) + } + + const coreKpis = data.kpis.slice(0, 4) + const moreKpis = data.kpis.slice(4) + + return ( +
+
+
+ 经营驾驶舱 + +
+

数据日期 {data.businessDate}

+

上个工作日经营简报

+

{data.summary}

+
+ 上个工作日成交额 + {formatMoney(data.kpis[0]?.value)} + 生成时间:{data.generatedAt} +
+
+ +
+ {coreKpis.map((metric) => ( + + ))} +
+ +
+
+
+

More

+

更多经营指标

+
+
+
+ {moreKpis.map((metric) => ( + + ))} +
+
+ + + +
+
+
+

Trend

+

近 7 天交易趋势

+
+
+ +
+ +
+
+
+

Fund

+

资金池摘要

+
+
+
+ {data.fundPool.map((metric) => ( + + ))} +
+
+ + + + + +
+ ) +} diff --git a/dashboard-frontend/src/features/boss-dashboard/pages/OperationsPages.tsx b/dashboard-frontend/src/features/boss-dashboard/pages/OperationsPages.tsx new file mode 100644 index 0000000..26b37e0 --- /dev/null +++ b/dashboard-frontend/src/features/boss-dashboard/pages/OperationsPages.tsx @@ -0,0 +1,468 @@ +import { Button, CapsuleTabs, DotLoading, ErrorBlock, Tag, Toast } from 'antd-mobile' +import { useMemo, useState } from 'react' +import { MiniTrendChart } from '../../../components/charts/MiniTrendChart' +import { KpiCard } from '../../../components/kpi/KpiCard' +import { formatMoney, formatNumber } from '../../../utils/format' +import { downloadDailyReportArchive, useDashboardOverview } from '../api' +import type { DashboardOverview, RiskLevel, SnapshotSlot, TodaySnapshot } from '../types' + +const snapshotStatusMeta = { + pending: { color: 'default', label: '待生成' }, + success: { color: 'success', label: '已生成' }, + failed: { color: 'danger', label: '失败' }, + temporary: { color: 'warning', label: '临时' }, +} as const + +const riskLevelMeta: Record = { + red: { color: 'danger', label: '红色' }, + yellow: { color: 'warning', label: '黄色' }, + gray: { color: 'default', label: '灰色' }, +} + +const snapshotSlotMeta: Record< + SnapshotSlot, + { + title: string + subtitle: string + metricLabels: { + primaryUsers: string + primaryOrders: string + amount: string + paidAmount: string + merchandise: string + bonus: string + } + checklist: string[] + } +> = { + '1015': { + title: '上午抢购快报', + subtitle: '用户集中抢购上一天用户寄卖的商品,重点看成交、付款和采购用户是否达标。', + metricLabels: { + primaryUsers: '抢购用户', + primaryOrders: '抢购订单', + amount: '抢购成交额', + paidAmount: '已支付金额', + merchandise: '成交商品', + bonus: '相关奖金', + }, + checklist: ['抢购成交额是否低于昨日同节点', '采购用户是否异常回落', '付款金额与成交额是否明显偏离', '高货值寄卖商品是否完成消化'], + }, + '1455': { + title: '下午寄卖/转卖快报', + subtitle: '用户把上午抢到的商品继续寄卖或转卖,重点看新增寄售供给和奖金变化是否正常。', + metricLabels: { + primaryUsers: '寄卖用户', + primaryOrders: '转卖订单', + amount: '转卖成交额', + paidAmount: '回款金额', + merchandise: '新增寄售', + bonus: '奖金变化', + }, + checklist: ['抢购商品是否按预期转入寄卖', '新增寄售商品是否满足下午供给', '个人奖金与推广奖金是否同步变化', '转卖回款是否出现异常延迟'], + }, +} + +function QueryState({ + isLoading, + isError, + refetch, + title, +}: { + isLoading: boolean + isError: boolean + refetch: () => void + title: string +}) { + if (isLoading) { + return ( +
+ +

正在加载{title}...

+
+ ) + } + + if (isError) { + return ( +
+ + +
+ ) + } + + return null +} + +function OperationsHeader({ + kicker, + title, + description, + extra, +}: { + kicker: string + title: string + description: string + extra?: string +}) { + return ( +
+

{kicker}

+

{title}

+

{description}

+ {extra && {extra}} +
+ ) +} + +function SnapshotDetailCard({ snapshot }: { snapshot: TodaySnapshot }) { + const status = snapshotStatusMeta[snapshot.status] + const slotMeta = snapshotSlotMeta[snapshot.slot] + + return ( +
+
+
+

{snapshot.slot}

+

{slotMeta.title}

+
+ {status.label} +
+

{slotMeta.subtitle}

+

{snapshot.message}

+ {snapshot.generatedAt &&

生成时间:{snapshot.generatedAt}

} +
+ + {slotMeta.metricLabels.primaryUsers} + {formatNumber(snapshot.purchaseUsers)}人 + + + {slotMeta.metricLabels.primaryOrders} + {formatNumber(snapshot.orderCount)}单 + + + {slotMeta.metricLabels.amount} + {formatMoney(snapshot.dealAmount)} + + + {slotMeta.metricLabels.paidAmount} + {formatMoney(snapshot.paidAmount)} + + + {slotMeta.metricLabels.merchandise} + {formatNumber(snapshot.newMerchandiseCount)}件 + + + {slotMeta.metricLabels.bonus} + {formatMoney(Number(snapshot.selfBonusChange) + Number(snapshot.shareBonusChange))} + +
+
+ ) +} + +function buildDailyReports(data: DashboardOverview) { + return data.trends + .slice(-4) + .reverse() + .map((trend, index) => ({ + ...trend, + status: index === 0 ? '已生成' : '历史快照', + bonusRate: Number(trend.amount) > 0 ? (Number(trend.bonus) / Number(trend.amount)) * 100 : 0, + })) +} + +export function DailyReportPage() { + const { data, isLoading, isError, refetch } = useDashboardOverview() + const [isArchiving, setIsArchiving] = useState(false) + const state = void refetch()} title="经营日报" /> + + if (!data) return state + + const reports = buildDailyReports(data) + + const handleArchive = async () => { + try { + setIsArchiving(true) + const blob = await downloadDailyReportArchive(data.businessDate) + const url = URL.createObjectURL(blob) + const link = document.createElement('a') + link.href = url + link.download = `dashboard-daily-report-${data.businessDate}.html` + document.body.appendChild(link) + link.click() + link.remove() + URL.revokeObjectURL(url) + Toast.show({ icon: 'success', content: '归档 HTML 已生成' }) + } catch { + Toast.show({ icon: 'fail', content: '归档生成失败,请稍后重试' }) + } finally { + setIsArchiving(false) + } + } + + return ( +
+ + +
+
+
+

Workday

+

上个工作日重点

+
+ 已归档 +
+
+ {data.kpis.slice(0, 4).map((metric) => ( + + ))} +
+
+ +
+
+
+

Trend

+

最近 7 天趋势

+
+
+ +
+ +
+
+
+

Archive

+

日报归档

+
+ +
+
+ {reports.map((report) => ( + + ))} +
+
+
+ ) +} + +export function TodaySnapshotPage() { + const { data, isLoading, isError, refetch } = useDashboardOverview() + const state = void refetch()} title="今日快报" /> + + if (!data) return state + + return ( +
+ + +
+
+
+

Timeline

+

节点快报

+
+
+
+ {data.snapshots.map((snapshot) => ( + + ))} +
+
+ +
+
+
+

Checklist

+

节点检查项

+
+
+
+ {data.snapshots.flatMap((snapshot) => + snapshotSlotMeta[snapshot.slot].checklist.map((item) => ( + + {snapshot.slot === '1015' ? '上午' : '下午'} + {item} + + )), + )} +
+
+
+ ) +} + +export function RiskCenterPage() { + const { data, isLoading, isError, refetch } = useDashboardOverview() + const [activeLevel, setActiveLevel] = useState('all') + const state = void refetch()} title="风险中心" /> + + const filteredRisks = useMemo(() => { + if (!data) return [] + if (activeLevel === 'all') return data.risks + return data.risks.filter((risk) => risk.level === activeLevel) + }, [activeLevel, data]) + + if (!data) return state + + const dangerousFunds = data.fundPool.filter((metric) => metric.status === 'warning' || metric.status === 'danger') + + return ( +
+ + +
+ {(['red', 'yellow', 'gray'] as const).map((level) => { + const meta = riskLevelMeta[level] + const count = data.risks.filter((risk) => risk.level === level).length + return ( + + ) + })} +
+ +
+ setActiveLevel(key as RiskLevel | 'all')}> + + + + + +
+ {filteredRisks.map((risk) => { + const meta = riskLevelMeta[risk.level] + return ( + + ) + })} +
+
+ +
+
+
+

Fund Watch

+

资金池关注项

+
+
+
+ {dangerousFunds.map((metric) => ( + + ))} +
+
+
+ ) +} + +export function ProfilePage() { + const { data, isLoading, isError, refetch } = useDashboardOverview() + const state = void refetch()} title="我的" /> + + if (!data) return state + + return ( +
+ + +
+ +
+

老板视角

+

可查看经营概览、今日快报、排行与风险预警。

+
+
+ +
+
+
+

Environment

+

数据环境

+
+ Mock +
+
+ + 数据日期 + {data.businessDate} + + + 生成时间 + {data.generatedAt} + + + API 模式 + {import.meta.env.VITE_MOCK_ENABLED === 'false' ? '真实接口' : 'Mock 演示'} + +
+
+ +
+
+
+

Permissions

+

权限模块

+
+
+
+ 经营概览:可见 + 资金池摘要:可见 + 风险预警:可见 + 导出能力:待接入 +
+
+
+ ) +} diff --git a/dashboard-frontend/src/features/boss-dashboard/types.ts b/dashboard-frontend/src/features/boss-dashboard/types.ts new file mode 100644 index 0000000..b17f8ac --- /dev/null +++ b/dashboard-frontend/src/features/boss-dashboard/types.ts @@ -0,0 +1,72 @@ +export type MetricStatus = 'normal' | 'success' | 'warning' | 'danger' + +export type SnapshotStatus = 'pending' | 'success' | 'failed' | 'temporary' + +export type SnapshotSlot = '1015' | '1455' + +export type KpiMetric = { + key: string + title: string + value: number | string | null + unit?: string + trendLabel?: string + trendValue?: number | string + status: MetricStatus + featured?: boolean +} + +export type TodaySnapshot = { + slot: SnapshotSlot + title: string + status: SnapshotStatus + generatedAt?: string + message: string + purchaseUsers: number + orderCount: number + dealAmount: number | string + paidAmount: number | string + newMerchandiseCount: number + selfBonusChange: number | string + shareBonusChange: number | string +} + +export type TrendPoint = { + date: string + amount: number | string + orders: number + newUsers: number + bonus: number | string +} + +export type RankItem = { + id: string + name: string + value: number | string + description: string + badge?: string +} + +export type RiskLevel = 'red' | 'yellow' | 'gray' + +export type RiskAlert = { + id: string + level: RiskLevel + type: string + title: string + description: string + discoveredAt: string +} + +export type DashboardOverview = { + businessDate: string + generatedAt: string + summary: string + kpis: KpiMetric[] + fundPool: KpiMetric[] + snapshots: TodaySnapshot[] + trends: TrendPoint[] + userRanks: RankItem[] + teamRanks: RankItem[] + productRanks: RankItem[] + risks: RiskAlert[] +} diff --git a/dashboard-frontend/src/features/common/PlaceholderPage.tsx b/dashboard-frontend/src/features/common/PlaceholderPage.tsx new file mode 100644 index 0000000..e5ccc7d --- /dev/null +++ b/dashboard-frontend/src/features/common/PlaceholderPage.tsx @@ -0,0 +1,18 @@ +import { Empty } from 'antd-mobile' + +type PlaceholderPageProps = { + title: string + description: string +} + +export function PlaceholderPage({ title, description }: PlaceholderPageProps) { + return ( +
+
+

经营驾驶舱

+

{title}

+
+ +
+ ) +} diff --git a/dashboard-frontend/src/index.css b/dashboard-frontend/src/index.css new file mode 100644 index 0000000..9bd4109 --- /dev/null +++ b/dashboard-frontend/src/index.css @@ -0,0 +1,589 @@ +:root { + --bg: #fff6f1; + --surface: #ffffff; + --surface-soft: #f6f9fb; + --text: #132033; + --muted: #6b7a90; + --border: rgba(19, 32, 51, 0.08); + --primary: #ff5b36; + --primary-deep: #f04a2a; + --primary-soft: #fff0eb; + --success: #14a46c; + --warning: #ffb000; + --danger: #dc2626; + --shadow: 0 16px 40px rgba(255, 91, 54, 0.14); + --radius-xl: 28px; + --radius-lg: 20px; + --radius-md: 14px; + font-family: + ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; + --adm-color-primary: var(--primary); + color: var(--text); + background: var(--bg); + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +* { + box-sizing: border-box; +} + +body { + margin: 0; + min-width: 320px; + background: + radial-gradient(circle at top left, rgba(255, 91, 54, 0.2), transparent 28rem), + var(--bg); +} + +button { + font: inherit; +} + +button:focus-visible { + outline: 2px solid rgba(255, 91, 54, 0.72); + outline-offset: 2px; +} + +#root { + width: min(100%, 430px); + min-height: 100svh; + margin: 0 auto; + background: var(--bg); + box-shadow: 0 0 0 1px rgba(19, 32, 51, 0.04); +} + +.mobile-shell { + min-height: 100svh; + position: relative; +} + +.mobile-main { + min-height: 100svh; + padding-bottom: calc(74px + env(safe-area-inset-bottom)); +} + +.bottom-nav { + position: fixed; + right: 0; + bottom: 0; + left: 0; + z-index: 20; + width: min(100%, 430px); + margin: 0 auto; + background: rgba(255, 255, 255, 0.94); + border-top: 1px solid var(--border); + backdrop-filter: blur(16px); +} + +.dashboard-page { + padding: 14px 14px 24px; +} + +.dashboard-hero { + position: relative; + overflow: hidden; + padding: 20px; + color: #fff; + background: + linear-gradient(145deg, rgba(255, 91, 54, 0.98), rgba(255, 139, 82, 0.92)), + radial-gradient(circle at 90% 10%, rgba(255, 176, 0, 0.42), transparent 18rem); + border-radius: 0 0 var(--radius-xl) var(--radius-xl); + box-shadow: var(--shadow); +} + +.hero-topline, +.section-title-row, +.risk-header { + display: flex; + align-items: center; + justify-content: space-between; + gap: 12px; +} + +.hero-topline span, +.eyebrow, +.section-kicker { + font-size: 12px; + font-weight: 700; + letter-spacing: 0.08em; + text-transform: uppercase; +} + +.hero-topline button { + min-height: 34px; + padding: 0 14px; + color: #fff; + background: rgba(255, 255, 255, 0.16); + border: 1px solid rgba(255, 255, 255, 0.24); + border-radius: 999px; +} + +.dashboard-hero h1 { + margin: 18px 0 8px; + font-size: 30px; + line-height: 1.1; +} + +.hero-summary { + margin: 0; + color: rgba(255, 255, 255, 0.78); + font-size: 14px; + line-height: 1.6; +} + +.hero-metric { + margin-top: 20px; + padding: 16px; + background: rgba(255, 255, 255, 0.14); + border: 1px solid rgba(255, 255, 255, 0.18); + border-radius: var(--radius-lg); +} + +.hero-metric span, +.hero-metric small { + display: block; + color: rgba(255, 255, 255, 0.72); + font-size: 12px; +} + +.hero-metric strong { + display: block; + margin: 6px 0; + font-size: 32px; + line-height: 1; +} + +.kpi-grid { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 10px; + margin-top: 12px; +} + +.kpi-card { + min-height: 112px; + padding: 14px; + text-align: left; + background: var(--surface); + border: 1px solid var(--border); + border-radius: var(--radius-lg); + box-shadow: 0 10px 28px rgba(22, 47, 80, 0.08); +} + +.kpi-card--featured { + grid-column: span 2; +} + +.kpi-title, +.kpi-trend, +.section-kicker, +.snapshot-time, +.rank-content small, +.risk-item p { + margin: 0; + color: var(--muted); +} + +.kpi-value { + display: block; + margin-top: 8px; + color: var(--text); + font-size: 22px; + line-height: 1.08; + word-break: break-all; +} + +.kpi-trend { + margin-top: 8px; + font-size: 12px; +} + +.kpi-trend span { + margin-left: 6px; + color: var(--primary); + font-weight: 700; +} + +.kpi-card--success .kpi-trend span { + color: var(--success); +} + +.kpi-card--warning .kpi-trend span { + color: var(--warning); +} + +.kpi-card--danger .kpi-value, +.kpi-card--danger .kpi-trend span { + color: var(--danger); +} + +.section-block { + margin-top: 14px; + padding: 16px; + background: var(--surface); + border: 1px solid var(--border); + border-radius: var(--radius-xl); + box-shadow: 0 10px 28px rgba(22, 47, 80, 0.08); +} + +.section-title-row h2 { + margin: 2px 0 0; + font-size: 18px; +} + +.compact-section .kpi-grid { + margin-top: 12px; +} + +.snapshot-section .adm-capsule-tabs { + margin: 14px 0; +} + +.snapshot-card { + padding: 14px; + background: var(--surface-soft); + border-radius: var(--radius-lg); +} + +.snapshot-message { + margin: 0 0 8px; + font-weight: 700; + line-height: 1.5; +} + +.snapshot-grid { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 10px; + margin-top: 14px; +} + +.snapshot-grid span { + min-height: 64px; + padding: 10px; + color: var(--muted); + font-size: 12px; + background: #fff; + border-radius: var(--radius-md); +} + +.snapshot-grid strong { + display: block; + margin-top: 5px; + color: var(--text); + font-size: 15px; +} + +.mini-trend-chart { + width: 100%; + height: 240px; +} + +.text-button, +.rank-item, +.risk-item { + appearance: none; + border: 0; + background: none; +} + +.text-button { + display: inline-flex; + align-items: center; + min-height: 36px; + color: var(--primary); + font-size: 13px; + font-weight: 700; +} + +.text-button:disabled { + color: var(--muted); + cursor: not-allowed; +} + +.rank-list, +.risk-list { + display: grid; + gap: 10px; + margin-top: 14px; +} + +.rank-item, +.risk-item { + width: 100%; + min-height: 58px; + padding: 12px; + text-align: left; + background: var(--surface-soft); + border-radius: var(--radius-md); +} + +.rank-item { + display: grid; + grid-template-columns: 28px 1fr auto; + align-items: center; + gap: 10px; +} + +.rank-index { + display: inline-grid; + width: 26px; + height: 26px; + place-items: center; + color: #fff; + font-weight: 800; + background: var(--text); + border-radius: 10px; +} + +.rank-content strong, +.rank-content small { + display: block; +} + +.rank-value { + color: var(--primary); + font-size: 13px; + font-weight: 800; +} + +.risk-count { + color: var(--danger); + font-weight: 800; +} + +.risk-header { + justify-content: flex-start; + color: var(--muted); + font-size: 12px; +} + +.risk-header time { + margin-left: auto; +} + +.risk-item strong { + display: block; + margin: 10px 0 4px; + font-size: 15px; +} + +.risk-item p { + line-height: 1.5; +} + +.placeholder-page, +.loading-page, +.error-page { + padding: 24px 16px; +} + +.mobile-page-header h1 { + margin: 4px 0 24px; +} + +.operations-page { + padding: 14px 14px 24px; +} + +.operations-header { + position: relative; + overflow: hidden; + padding: 20px; + color: #fff; + background: + linear-gradient(145deg, rgba(255, 91, 54, 0.98), rgba(255, 139, 82, 0.92)), + radial-gradient(circle at 90% 10%, rgba(255, 176, 0, 0.42), transparent 18rem); + border-radius: 0 0 var(--radius-xl) var(--radius-xl); + box-shadow: var(--shadow); +} + +.operations-header h1 { + margin: 12px 0 8px; + font-size: 28px; + line-height: 1.12; +} + +.operations-header p { + margin: 0; + color: rgba(255, 255, 255, 0.76); + line-height: 1.6; +} + +.operations-header .eyebrow { + color: rgba(255, 255, 255, 0.68); +} + +.operations-header span { + display: inline-flex; + margin-top: 16px; + padding: 7px 12px; + color: rgba(255, 255, 255, 0.86); + font-size: 12px; + font-weight: 700; + background: rgba(255, 255, 255, 0.14); + border: 1px solid rgba(255, 255, 255, 0.18); + border-radius: 999px; +} + +.report-list, +.check-list, +.info-list { + display: grid; + gap: 10px; + margin-top: 14px; +} + +.report-item { + display: grid; + grid-template-columns: 82px 1fr; + gap: 12px; + width: 100%; + padding: 13px; + text-align: left; + background: var(--surface-soft); + border: 0; + border-radius: var(--radius-md); +} + +.report-item span, +.info-list span { + display: grid; + gap: 4px; +} + +.report-item small, +.info-list small, +.profile-card p { + color: var(--muted); + line-height: 1.45; +} + +.snapshot-stack { + display: grid; + gap: 12px; + margin-top: 14px; +} + +.snapshot-detail-card { + padding: 14px; + background: var(--surface-soft); + border-radius: var(--radius-lg); +} + +.snapshot-detail-subtitle { + margin: 12px 0 0; + color: var(--muted); + font-size: 13px; + line-height: 1.55; +} + +.snapshot-detail-message { + margin: 10px 0 8px; + font-weight: 700; + line-height: 1.55; +} + +.snapshot-grid--wide { + grid-template-columns: repeat(2, minmax(0, 1fr)); +} + +.check-list span, +.info-list span { + padding: 12px; + background: var(--surface-soft); + border-radius: var(--radius-md); +} + +.check-list span { + color: var(--text); + font-weight: 700; +} + +.check-list strong { + margin-right: 8px; + color: var(--primary); +} + +.risk-summary-grid { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 10px; + margin-top: 12px; +} + +.risk-summary-card { + display: grid; + min-height: 104px; + padding: 12px; + text-align: left; + background: var(--surface); + border: 1px solid var(--border); + border-radius: var(--radius-lg); + box-shadow: 0 10px 28px rgba(22, 47, 80, 0.08); +} + +.risk-summary-card strong { + margin-top: 8px; + font-size: 28px; + line-height: 1; +} + +.risk-summary-card span:last-child { + color: var(--muted); + font-size: 12px; +} + +.risk-summary-card--red strong { + color: var(--danger); +} + +.risk-summary-card--yellow strong { + color: var(--warning); +} + +.risk-summary-card--gray strong { + color: var(--muted); +} + +.profile-card { + display: grid; + grid-template-columns: 58px 1fr; + align-items: center; + gap: 14px; + margin-top: 14px; + padding: 16px; + background: var(--surface); + border: 1px solid var(--border); + border-radius: var(--radius-xl); + box-shadow: 0 10px 28px rgba(22, 47, 80, 0.08); +} + +.profile-card h2, +.profile-card p { + margin: 0; +} + +.profile-avatar { + display: grid; + width: 58px; + height: 58px; + place-items: center; + color: #fff; + font-size: 22px; + font-weight: 800; + background: linear-gradient(145deg, var(--primary), var(--warning)); + border-radius: 22px; +} + +.loading-page, +.error-page { + display: grid; + min-height: 60svh; + place-content: center; + gap: 14px; + text-align: center; +} diff --git a/dashboard-frontend/src/main.tsx b/dashboard-frontend/src/main.tsx new file mode 100644 index 0000000..568f626 --- /dev/null +++ b/dashboard-frontend/src/main.tsx @@ -0,0 +1,20 @@ +import { StrictMode } from 'react' +import { createRoot } from 'react-dom/client' +import 'antd-mobile/es/global' +import './index.css' +import App from './App.tsx' + +const startApp = async () => { + if (import.meta.env.VITE_MOCK_ENABLED !== 'false') { + const { worker } = await import('./services/mock/browser') + await worker.start({ onUnhandledRequest: 'bypass' }) + } + + createRoot(document.getElementById('root')!).render( + + + , + ) +} + +void startApp() diff --git a/dashboard-frontend/src/services/http/client.ts b/dashboard-frontend/src/services/http/client.ts new file mode 100644 index 0000000..ee09987 --- /dev/null +++ b/dashboard-frontend/src/services/http/client.ts @@ -0,0 +1,26 @@ +import axios from 'axios' + +export const httpClient = axios.create({ + baseURL: import.meta.env.VITE_API_BASE_URL ?? '', + timeout: 8000, +}) + +export type ApiResponse = { + code: number + message?: string + msg?: string + data: T +} + +export async function getApiData(url: string): Promise { + const response = await httpClient.get>(url) + if (response.data.code !== 0 && response.data.code !== 200) { + throw new Error(response.data.msg ?? response.data.message ?? '接口请求失败') + } + return response.data.data +} + +export async function getBlob(url: string): Promise { + const response = await httpClient.get(url, { responseType: 'blob' }) + return response.data +} diff --git a/dashboard-frontend/src/services/mock/browser.ts b/dashboard-frontend/src/services/mock/browser.ts new file mode 100644 index 0000000..4dd03f0 --- /dev/null +++ b/dashboard-frontend/src/services/mock/browser.ts @@ -0,0 +1,4 @@ +import { setupWorker } from 'msw/browser' +import { handlers } from './handlers' + +export const worker = setupWorker(...handlers) diff --git a/dashboard-frontend/src/services/mock/handlers.ts b/dashboard-frontend/src/services/mock/handlers.ts new file mode 100644 index 0000000..e8748c3 --- /dev/null +++ b/dashboard-frontend/src/services/mock/handlers.ts @@ -0,0 +1,58 @@ +import { http, HttpResponse } from 'msw' +import { dashboardMock } from '../../features/boss-dashboard/mock' + +function buildArchiveHtml() { + return ` + + + + + 经营日报归档 - ${dashboardMock.businessDate} + + + +
+
+

Daily Report Archive

+

经营日报归档

+

${dashboardMock.summary}

+ 数据日期:${dashboardMock.businessDate} / 生成时间:${dashboardMock.generatedAt} +
+
+

核心经营指标

+
+ ${dashboardMock.kpis + .map((metric) => `
${metric.title}

${metric.value}${metric.unit ?? ''}

${metric.trendLabel ?? ''}
`) + .join('')} +
+
+
+ +` +} + +export const handlers = [ + http.get('/api/admin/dashboard/overview', () => { + return HttpResponse.json({ + code: 0, + msg: 'success', + data: dashboardMock, + }) + }), + http.get('/api/admin/dashboard/daily-report/archive', () => { + return new HttpResponse(buildArchiveHtml(), { + headers: { + 'Content-Type': 'text/html; charset=utf-8', + 'Content-Disposition': `attachment; filename="dashboard-daily-report-${dashboardMock.businessDate}.html"`, + }, + }) + }), +] diff --git a/dashboard-frontend/src/utils/format.test.ts b/dashboard-frontend/src/utils/format.test.ts new file mode 100644 index 0000000..e935f79 --- /dev/null +++ b/dashboard-frontend/src/utils/format.test.ts @@ -0,0 +1,22 @@ +import { describe, expect, it } from 'vitest' +import { formatMetricValue, formatMoney, formatNumber, formatTrend } from './format' + +describe('format helpers', () => { + it('formats money with yuan symbol and two decimals', () => { + expect(formatMoney(1289360.4)).toBe('¥1,289,360.40') + }) + + it('formats metric values based on unit', () => { + expect(formatMetricValue(418471.07, '分')).toBe('418,471.070') + expect(formatMetricValue(936, '人')).toBe('936人') + }) + + it('uses placeholder for empty values', () => { + expect(formatNumber(null)).toBe('--') + }) + + it('adds plus sign for positive trend values', () => { + expect(formatTrend(8.6)).toBe('+8.6%') + expect(formatTrend(-3.2)).toBe('-3.2%') + }) +}) diff --git a/dashboard-frontend/src/utils/format.ts b/dashboard-frontend/src/utils/format.ts new file mode 100644 index 0000000..50310d0 --- /dev/null +++ b/dashboard-frontend/src/utils/format.ts @@ -0,0 +1,33 @@ +export function formatMoney(value: number | string | null | undefined): string { + if (value === null || value === undefined || value === '') return '--' + const numberValue = Number(value) + if (Number.isNaN(numberValue)) return String(value) + return `¥${numberValue.toLocaleString('zh-CN', { + minimumFractionDigits: 2, + maximumFractionDigits: 2, + })}` +} + +export function formatNumber(value: number | string | null | undefined, digits = 0): string { + if (value === null || value === undefined || value === '') return '--' + const numberValue = Number(value) + if (Number.isNaN(numberValue)) return String(value) + return numberValue.toLocaleString('zh-CN', { + minimumFractionDigits: digits, + maximumFractionDigits: digits, + }) +} + +export function formatMetricValue(value: number | string | null, unit?: string): string { + if (unit === '元') return formatMoney(value) + if (unit === '分') return formatNumber(value, 3) + return `${formatNumber(value)}${unit ?? ''}` +} + +export function formatTrend(value?: number | string): string { + if (value === undefined || value === '') return '' + const numberValue = Number(value) + if (Number.isNaN(numberValue)) return String(value) + const prefix = numberValue > 0 ? '+' : '' + return `${prefix}${numberValue.toFixed(1)}%` +} diff --git a/dashboard-frontend/tsconfig.app.json b/dashboard-frontend/tsconfig.app.json new file mode 100644 index 0000000..1d29c88 --- /dev/null +++ b/dashboard-frontend/tsconfig.app.json @@ -0,0 +1,25 @@ +{ + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", + "target": "es2023", + "lib": ["ES2023", "DOM", "DOM.Iterable"], + "module": "esnext", + "types": ["vite/client"], + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "moduleDetection": "force", + "noEmit": true, + "jsx": "react-jsx", + + /* Linting */ + "noUnusedLocals": true, + "noUnusedParameters": true, + "erasableSyntaxOnly": true, + "noFallthroughCasesInSwitch": true + }, + "include": ["src"] +} diff --git a/dashboard-frontend/tsconfig.json b/dashboard-frontend/tsconfig.json new file mode 100644 index 0000000..1ffef60 --- /dev/null +++ b/dashboard-frontend/tsconfig.json @@ -0,0 +1,7 @@ +{ + "files": [], + "references": [ + { "path": "./tsconfig.app.json" }, + { "path": "./tsconfig.node.json" } + ] +} diff --git a/dashboard-frontend/tsconfig.node.json b/dashboard-frontend/tsconfig.node.json new file mode 100644 index 0000000..d3c52ea --- /dev/null +++ b/dashboard-frontend/tsconfig.node.json @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", + "target": "es2023", + "lib": ["ES2023"], + "module": "esnext", + "types": ["node"], + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "moduleDetection": "force", + "noEmit": true, + + /* Linting */ + "noUnusedLocals": true, + "noUnusedParameters": true, + "erasableSyntaxOnly": true, + "noFallthroughCasesInSwitch": true + }, + "include": ["vite.config.ts"] +} diff --git a/dashboard-frontend/vite.config.ts b/dashboard-frontend/vite.config.ts new file mode 100644 index 0000000..8cc3e25 --- /dev/null +++ b/dashboard-frontend/vite.config.ts @@ -0,0 +1,17 @@ +import { defineConfig } from 'vite' +import react from '@vitejs/plugin-react' + +// https://vite.dev/config/ +export default defineConfig({ + plugins: [react()], + server: { + host: '0.0.0.0', + port: 5174, + proxy: { + '/api': { + target: 'http://localhost:30032', + changeOrigin: true, + }, + }, + }, +})