import { type ReactNode, useEffect, useState } from "react"; import { Link } from "react-router-dom"; import { useQuery } from "@tanstack/react-query"; import { mapApiErrorMessage } from "../api/errorMessages"; import { fetchPortalHome, type PortalHomePayload, type PortalRecentActivity, type PortalTeamStatusItem, } from "../api/portal"; import { fetchWorkflowTaskSummary, type WorkflowTaskProductivitySummary } from "../api/workflow"; import { isApiError } from "../api/types"; import { useAuth } from "../auth/AuthContext"; import { hasAnyPermission, isManagerRole } from "../auth/permissions"; import { EmployeeAvatar } from "../components/common/EmployeeAvatar"; import { ErrorBoundary } from "../components/common/ErrorBoundary"; import { EmptyStateCard } from "../components/common/EmptyStateCard"; import { WelcomeDialog } from "../components/onboarding/WelcomeDialog"; import { MetricCard } from "../components/patterns/MetricCard"; import { PageScaffold } from "../components/patterns/PageScaffold"; import { SectionPanel } from "../components/patterns/SectionPanel"; import { WorkbenchHero } from "../components/patterns/WorkbenchHero"; import { Button } from "../components/ui/button"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "../components/ui/tabs"; import { employeePortalSchema, managerPortalSchema } from "../features/portal/schema"; import { clearWelcomeDialogFlag, dismissGettingStartedCard, shouldShowGettingStartedCard, shouldShowWelcomeDialog, } from "../lib/onboardingGuide"; import { readRecentVisits } from "../lib/recentVisits"; type SummaryItem = { key: string; href: string; label: string; value: string; note?: string; tone?: "normal" | "warning"; }; function workflowEntityLabel(entityType: string) { if (entityType === "reimbursement") return "报销"; if (entityType === "leave") return "请假"; if (entityType === "purchase_request") return "采购"; if (entityType === "travel_request") return "差旅"; if (entityType === "unknown") return "未知业务"; return entityType; } type TodoAggregateItem = { title: string; sourceLabel: string; severity: "normal" | "warning" | "high"; href: string; icon: string; }; type QuickActionConfig = { key: string; icon: string; label: string; description: string; to?: string; onClick?: () => void; disabled?: boolean; }; type GettingStartedChecklistItem = { key: string; title: string; description: string; href: string; completed: boolean; }; const LEAVE_PORTAL_PERMISSIONS = ["leave.view_own", "leave.view_all", "leave.create", "leave.submit"]; const EXPENSE_PORTAL_PERMISSIONS = ["expense.view_own", "expense.view_all", "expense.create", "expense.proxy_manage"]; const PROCUREMENT_PORTAL_PERMISSIONS = ["procurement.view_own", "procurement.view_all", "procurement.create", "procurement.submit"]; const TRAVEL_PORTAL_PERMISSIONS = ["travel.view_own", "travel.view_all", "travel.create", "travel.submit"]; const APPROVAL_PORTAL_PERMISSIONS = ["expense.approve", "leave.approve", "procurement.approve", "travel.approve"]; const FINANCE_PORTAL_PERMISSIONS = ["payment.batch.manage", "risk.review.manage", "expense.finance.pay"]; const SYSTEM_PORTAL_PERMISSIONS = ["system.users", "system.settings", "system.audit.view", "notification.channel.manage"]; const TODO_SEVERITY_RANK = { high: 3, warning: 2, normal: 1, } as const; function isSummaryItem(item: SummaryItem | null): item is SummaryItem { return Boolean(item); } function severityTone(severity?: "normal" | "warning" | "high") { if (severity === "high") return "danger" as const; if (severity === "warning") return "warning" as const; return "neutral" as const; } function buildFallbackRoleCapabilities(permissions: string[], roleCodes: string[]) { const hasAdminRole = roleCodes.includes("admin") || hasAnyPermission(permissions, ["*"]); const hasBusinessApproverRole = roleCodes.some((roleCode) => ["manager", "ceo", "finance", "finance_mgr", "hr"].includes(roleCode), ); const hasFinanceRole = roleCodes.some((roleCode) => ["finance", "finance_mgr"].includes(roleCode)); return { canSubmitApplications: hasAnyPermission(permissions, [ ...LEAVE_PORTAL_PERMISSIONS, ...EXPENSE_PORTAL_PERMISSIONS, ...PROCUREMENT_PORTAL_PERMISSIONS, ...TRAVEL_PORTAL_PERMISSIONS, ]), canApproveTasks: hasBusinessApproverRole && hasAnyPermission(permissions, APPROVAL_PORTAL_PERMISSIONS), canUseFinance: hasFinanceRole && hasAnyPermission(permissions, FINANCE_PORTAL_PERMISSIONS), canManageSystem: hasAdminRole || hasAnyPermission(permissions, SYSTEM_PORTAL_PERMISSIONS), canViewTeam: hasBusinessApproverRole || hasAnyPermission(permissions, ["report.dashboard.view", "system.users", "project.manage"]), isAdminOnly: hasAdminRole && !hasBusinessApproverRole, }; } function pickCards(cards: PortalHomePayload["cards"], keys: string[]) { const map = new Map(cards.map((card) => [card.key, card])); return keys.map((key) => map.get(key)).filter((card): card is PortalHomePayload["cards"][number] => Boolean(card)); } function compactCards(cards: PortalHomePayload["cards"], excludedKeys: string[]) { return cards.filter((card) => !excludedKeys.includes(card.key)); } function formatWeeklyStatus(payload: PortalHomePayload["weeklyReport"]) { if (!payload) return "未启用"; return payload.status === "submitted" ? "已提交" : "待填写"; } function formatRelativeTime(value: string | null) { if (!value) { return "-"; } const timestamp = new Date(value).getTime(); if (Number.isNaN(timestamp)) { return "-"; } const diffMs = Date.now() - timestamp; if (diffMs < 60 * 1000) { return "刚刚"; } const diffMinutes = Math.floor(diffMs / (60 * 1000)); if (diffMinutes < 60) { return `${diffMinutes} 分钟前`; } const diffHours = Math.floor(diffMinutes / 60); if (diffHours < 24) { return `${diffHours} 小时前`; } const diffDays = Math.floor(diffHours / 24); return `${diffDays} 天前`; } function buildTodoAggregateItems(payload: PortalHomePayload) { const cards = Array.isArray(payload.cards) ? payload.cards : []; const items: TodoAggregateItem[] = []; if (payload.pendingTasks.count > 0) { items.push({ title: `审批待办 ${payload.pendingTasks.count} 条`, sourceLabel: "审批", severity: "warning", href: payload.pendingTasks.href, icon: "fact_check", }); } if ((payload.todoSummary?.openCount ?? 0) > 0) { items.push({ title: `TODO 待办 ${payload.todoSummary?.openCount ?? 0} 条`, sourceLabel: "TODO", severity: (payload.todoSummary?.highPriorityCount ?? 0) > 0 ? "high" : "normal", href: payload.todoSummary?.href ?? "/todos", icon: "check_circle", }); } for (const card of cards) { if (!card.severity || (card.severity !== "warning" && card.severity !== "high")) { continue; } items.push({ title: `${card.title}:${card.value}`, sourceLabel: "工作台", severity: card.severity, href: card.href, icon: card.severity === "high" ? "priority_high" : "warning", }); } const deduped = new Map(); for (const item of items) { deduped.set(`${item.title}|${item.href}|${item.sourceLabel}`, item); } return Array.from(deduped.values()) .sort((left, right) => { const severityCompare = TODO_SEVERITY_RANK[right.severity] - TODO_SEVERITY_RANK[left.severity]; if (severityCompare !== 0) { return severityCompare; } return left.title.localeCompare(right.title, "zh-CN"); }) .slice(0, 10); } function resolveTeamStatusBadge(item: PortalTeamStatusItem) { if (item.onLeave) { return { label: item.leaveType ? `请假 · ${item.leaveType}` : "请假", className: "badge-warning", }; } if (item.presenceStatus === "office") { return { label: "在岗", className: "badge-success" }; } if (item.presenceStatus === "out") { return { label: "外出", className: "inline-flex items-center rounded-full bg-sky-50 px-2.5 py-0.5 text-xs font-semibold text-sky-700 ring-1 ring-inset ring-sky-600/10", }; } if (item.presenceStatus === "travel") { return { label: "差旅", className: "inline-flex items-center rounded-full bg-sky-50 px-2.5 py-0.5 text-xs font-semibold text-sky-700 ring-1 ring-inset ring-sky-600/10", }; } if (item.presenceStatus === "remote") { return { label: "居家", className: "badge-neutral" }; } return { label: "考勤", className: "inline-flex items-center rounded-full bg-slate-100 px-2.5 py-0.5 text-xs font-semibold text-slate-700 ring-1 ring-inset ring-slate-600/10", }; } function formatTeamStatusAttendance(item: PortalTeamStatusItem) { if (item.onLeave) { return item.leaveType ? `${item.leaveType} 已审批` : "今日请假"; } if (item.presenceStatus === "travel") { return "今日差旅"; } if (item.presenceStatus === "out") { return "当前外出"; } if (item.presenceStatus === "remote") { return "今日居家办公"; } return "企微考勤见考勤页"; } function SummaryPanel({ title, items }: { title: string; items: SummaryItem[] }) { if (!items.length) { return ; } return (
{items.map((item) => (
{item.label}
{item.value}
{item.note ?
{item.note}
: null} ))}
); } function FinanceSummaryPanel({ summary }: { summary: PortalHomePayload["financeSummary"] }) { if (!summary) { return null; } const items: SummaryItem[] = [ { key: "finance-pending-batches", href: summary.href, label: "待付款批次", value: `${summary.pendingBatchCount} 个`, tone: summary.pendingBatchCount > 0 ? "warning" : "normal", }, { key: "finance-failed-items", href: summary.href, label: "异常付款", value: `${summary.failedItemCount} 项`, tone: summary.failedItemCount > 0 ? "warning" : "normal", }, { key: "finance-risk-flags", href: summary.href, label: "待复核风险", value: `${summary.riskCount} 项`, tone: summary.riskCount > 0 ? "warning" : "normal", }, ]; return ; } function ApprovalProductivityPanel({ summary, title = "审批效率摘要", }: { summary: WorkflowTaskProductivitySummary | null; title?: string; }) { if (!summary) { return null; } const typeDistribution = summary.byEntityType .filter((item) => item.pendingTotal > 0) .map((item) => `${workflowEntityLabel(item.key)} ${item.pendingTotal}`) .join(" / "); const items: SummaryItem[] = [ { key: "workflow-pending-total", href: "/workflow/tasks", label: "当前待办", value: `${summary.pendingTotal} 条`, note: summary.earliestPendingAt ? `最早待处理:${formatRelativeTime(summary.earliestPendingAt)}` : "暂无待处理审批。", tone: summary.pendingTotal > 0 ? "warning" : "normal", }, { key: "workflow-overdue-total", href: "/workflow/tasks", label: "超时待办", value: `${summary.overdueTotal} 条`, note: `平均等待 ${summary.averagePendingAgeHours} 小时`, tone: summary.overdueTotal > 0 ? "warning" : "normal", }, { key: "workflow-entity-distribution", href: "/workflow/tasks", label: "业务分布", value: typeDistribution || "暂无", note: "按报销、请假、采购、差旅汇总。", }, ]; return ; } function AnnouncementsList({ announcements }: { announcements: PortalHomePayload["announcements"] }) { const safeAnnouncements = Array.isArray(announcements) ? announcements : []; return (
{safeAnnouncements.length ? safeAnnouncements.slice(0, 4).map((item) => (
{item.title}
{item.summary}
{item.href ? ( 查看详情 ) : null}
)) : }
); } function QuickLinksPanel({ items }: { items: PortalHomePayload["quickLinks"] }) { const safeItems = Array.isArray(items) ? items : []; return (
{safeItems.length ? safeItems.map((item) => ( {item.title} )) : }
); } function SecondaryCardsPanel({ title, cards }: { title: string; cards: PortalHomePayload["cards"] }) { if (!cards.length) { return ; } return (
{cards.map((card) => ( ))}
); } function CompactGroup({ title, children }: { title: string; children: ReactNode }) { return (

{title}

{children}
); } function CompactSummaryLinks({ title, items }: { title: string; items: SummaryItem[] }) { return ( {items.length ? items.map((item) => ( {item.label} {item.value} )) :
暂无摘要。
}
); } function CompactRecentVisits() { const recentVisits = readRecentVisits(); return ( {recentVisits.length ? recentVisits.map((item) => ( {item.title} 打开 )) :
暂无最近访问。
}
); } function CompactQuickLinks({ items }: { items: PortalHomePayload["quickLinks"] }) { const safeItems = Array.isArray(items) ? items : []; return ( {safeItems.length ? safeItems.slice(0, 6).map((item) => ( {item.title} 进入 )) :
暂无常用入口。
}
); } function CompactMetricLinks({ title, cards }: { title: string; cards: PortalHomePayload["cards"] }) { return ( {cards.length ? cards.slice(0, 6).map((card) => ( {card.title} {card.value} )) :
暂无统计项。
}
); } function AdoptionSummaryPanel({ adoptionSummary }: { adoptionSummary: NonNullable | null }) { if (!adoptionSummary) { return null; } return (
试点观测摘要
管理员按需查看。
展开 / 收起
近 7 天登录覆盖率
{adoptionSummary.login7dRate}%
周报提交率
{adoptionSummary.weeklySubmissionRate}%
团队状态覆盖率
{adoptionSummary.presenceCoverageRate}%
当前公告数
{adoptionSummary.announcementCount}
本周观测提示
    {adoptionSummary.hints.map((hint) => (
  • {hint}
  • ))}
); } function SecondaryWorkspacePanel({ title, description, children, }: { title: string; description: string; children: ReactNode; }) { return (
{title}
{description}
展开 / 收起
{children}
); } function PortalSectionFallback({ title, description, }: { title: string; description: string; }) { return ( ); } function GettingStartedCard({ items, completedCount, onDismiss, }: { items: GettingStartedChecklistItem[]; completedCount: number; onDismiss: () => void; }) { const progressPercent = items.length ? Math.round((completedCount / items.length) * 100) : 0; return ( 关闭清单 )} className="shadow-sm" >
{items.map((item) => ( {item.completed ? "check_circle" : "radio_button_unchecked"}
{item.title}
{item.description}
arrow_forward ))}
完成进度 {completedCount}/{items.length}
); } function QuickActionTile({ icon, label, description, to, onClick, disabled = false, compact = false, }: QuickActionConfig & { compact?: boolean }) { const baseClassName = disabled ? `portal-action-tile portal-action-tile--disabled${compact ? " portal-action-tile--compact" : ""}` : `portal-action-tile${compact ? " portal-action-tile--compact" : ""}`; const content = ( <>
{icon}
{label}
{description}
); if (to && !disabled) { return {content}; } if (onClick) { return ( ); } return
{content}
; } function QuickActionsPanel({ items, layout = "grid", }: { items: QuickActionConfig[]; layout?: "grid" | "rail"; }) { return (
{items.length ? items.map((item) => ( )) : }
); } function TodoAggregatePanel({ items }: { items: TodoAggregateItem[] }) { const visibleItems = items.slice(0, 5); const hiddenCount = Math.max(items.length - visibleItems.length, 0); return (
{visibleItems.length ? visibleItems.map((item) => (
{item.icon}
{item.title}
{item.sourceLabel} {item.severity === "high" ? 高优先 : null} {item.severity === "warning" ? 关注 : null}
进入
)) : }
{hiddenCount > 0 ? (
另有 {hiddenCount} 项。 查看全部待办
) : null}
); } function TeamStatusBoardPanel({ items }: { items: PortalTeamStatusItem[] }) { const safeItems = Array.isArray(items) ? items : []; const visibleItems = safeItems.slice(0, 5); const hiddenCount = Math.max(safeItems.length - visibleItems.length, 0); return ( 查看团队页} className="portal-panel-fill" >
{visibleItems.length ? visibleItems.map((item) => { const badge = resolveTeamStatusBadge(item); return (
{item.name}
{formatTeamStatusAttendance(item)}
{badge.label}
); }) : }
{hiddenCount > 0 ? (
另有 {hiddenCount} 位。 查看全部
) : null}
); } function RecentActivitiesList({ items }: { items: PortalRecentActivity[] }) { const safeItems = Array.isArray(items) ? items : []; return (
{safeItems.length ? safeItems.slice(0, 5).map((item) => (
{item.summary}
{formatRelativeTime(item.createdAt)}
)) : }
); } function ActivityAnnouncementsPanel({ activities, announcements, }: { activities: PortalRecentActivity[]; announcements: PortalHomePayload["announcements"]; }) { return ( 公告页} className="portal-panel-fill" > 动态 公告 ); } export function PortalHomePage() { const { me, permissions } = useAuth(); const [showWelcome, setShowWelcome] = useState(false); const [showGettingStarted, setShowGettingStarted] = useState(false); const portalQuery = useQuery({ queryKey: ["portal-home"], queryFn: () => fetchPortalHome() }); const workflowSummaryQuery = useQuery({ enabled: hasAnyPermission(permissions, APPROVAL_PORTAL_PERMISSIONS), queryKey: ["workflow", "tasks", "summary", "mine"], queryFn: () => fetchWorkflowTaskSummary(), }); const workflowSystemSummaryQuery = useQuery({ enabled: hasAnyPermission(permissions, ["system.audit.view"]), queryKey: ["workflow", "tasks", "summary", "system"], queryFn: () => fetchWorkflowTaskSummary({ scope: "system" }), }); const canSetupWorkspace = hasAnyPermission(permissions, ["system.settings"]); useEffect(() => { setShowWelcome(shouldShowWelcomeDialog()); setShowGettingStarted(shouldShowGettingStartedCard()); }, []); const gettingStartedItems: GettingStartedChecklistItem[] = [ { key: "profile", title: "完善个人资料", description: "补充紧急联系人和银行账户", href: "/settings/profile", completed: (me?.profileCompleteness?.score ?? 0) >= 50, }, { key: "notification-preferences", title: "设置通知偏好", description: "选择是否接收外部渠道推送", href: "/settings/profile", completed: true, }, { key: "leave-flow", title: "了解请假流程", description: "查看假期类型和余额", href: "/leave-requests", completed: false, }, { key: "notifications", title: "查看通知中心", description: "了解审批和业务消息", href: "/notifications", completed: false, }, ]; const gettingStartedCompletedCount = gettingStartedItems.filter((item) => item.completed).length; if (portalQuery.isLoading) { return
加载工作台中...
; } if (portalQuery.isError || !portalQuery.data) { const message = isApiError(portalQuery.error) ? `${portalQuery.error.code}: ${mapApiErrorMessage(portalQuery.error.code, portalQuery.error.details, portalQuery.error.message)}` : "工作台加载失败"; return
{message}
; } const payload = portalQuery.data; const cards = Array.isArray(payload.cards) ? payload.cards : []; const applicationItems = Array.isArray(payload.myApplications) ? payload.myApplications : []; const announcementItems = Array.isArray(payload.announcements) ? payload.announcements : []; const quickLinkItems = Array.isArray(payload.quickLinks) ? payload.quickLinks : []; const recentActivities = Array.isArray(payload.recentActivities) ? payload.recentActivities : []; const teamStatusBoard = Array.isArray(payload.teamStatusBoard) ? payload.teamStatusBoard : []; const roleCapabilities = payload.roleCapabilities ?? buildFallbackRoleCapabilities(permissions, me?.roleCodes ?? []); const isManagerView = roleCapabilities.canViewTeam || roleCapabilities.canApproveTasks || roleCapabilities.canUseFinance || roleCapabilities.canManageSystem || isManagerRole(permissions); const workspaceEyebrow = roleCapabilities.canUseFinance ? "财务工作台" : roleCapabilities.canManageSystem ? "管理工作台" : roleCapabilities.canApproveTasks ? "审批工作台" : "个人工作台"; const portalSchema = isManagerView ? managerPortalSchema : employeePortalSchema; const primaryCards = pickCards(cards, portalSchema.heroCards); const primaryCardKeySet = primaryCards.map((card) => card.key); const secondaryCards = compactCards(cards, primaryCardKeySet); const todoAggregateItems = buildTodoAggregateItems({ ...payload, cards }); function handleCloseWelcome() { clearWelcomeDialogFlag(); setShowWelcome(false); } function handleDismissGettingStarted() { dismissGettingStartedCard(); setShowGettingStarted(false); } const employeeSummaryItems = ([ { key: "expense", href: "/reimbursements", label: "我的报销", value: `${applicationItems.find((item) => item.domain === "reimbursement")?.count ?? 0} 条`, }, { key: "leave", href: "/leave-requests", label: "我的请假", value: `${applicationItems.find((item) => item.domain === "leave")?.count ?? 0} 条`, }, { key: "assets", href: "/assets", label: "我的资产", value: String(secondaryCards.find((card) => card.key === "my_assets")?.value ?? 0), }, payload.todayPresence ? { key: "today-status-note", href: payload.todayPresence.href, label: "今日状态", value: payload.todayPresence.label, note: payload.todayPresence.note ?? "可在团队页更新。", tone: payload.todayPresence.status === "leave" ? "warning" as const : "normal" as const, } : null, ] as Array).filter(isSummaryItem); const managerSummaryItems = ([ { key: "team-summary", href: payload.teamPresence?.href ?? "/team", label: "今日团队状态", value: payload.teamPresence ? `在岗 ${payload.teamPresence.office}/${payload.teamPresence.total}` : "待启用", note: payload.teamPresence ? `请假 ${payload.teamPresence.leave},未更新 ${payload.teamPresence.unspecified}` : "团队状态待启用。", tone: payload.teamPresence && (payload.teamPresence.leave > 0 || payload.teamPresence.unspecified > 0) ? "warning" as const : "normal" as const, }, payload.weeklySummary ? { key: "weekly-summary", href: payload.weeklySummary.href, label: "未提交周报", value: `${payload.weeklySummary.missingCount} 人`, note: `需协助 ${payload.weeklySummary.needsHelpCount} 人`, tone: payload.weeklySummary.missingCount > 0 ? "warning" as const : "normal" as const, } : null, ] as Array).filter(isSummaryItem); const approvalSummaryItems = ([ roleCapabilities.canApproveTasks ? { key: "approval-pending", href: payload.pendingTasks.href, label: "审批待办", value: `${payload.pendingTasks.count} 条`, note: payload.pendingTasks.count > 0 ? "进入审批中心处理。" : "暂无待处理审批。", tone: payload.pendingTasks.count > 0 ? "warning" as const : "normal" as const, } : null, roleCapabilities.canApproveTasks ? { key: "approval-completed", href: "/workflow/tasks?status=completed", label: "已处理", value: "查看", note: "复核历史处理记录。", } : null, ] as Array).filter(isSummaryItem); const systemSummaryItems = ([ roleCapabilities.canManageSystem ? { key: "system-users", href: "/people", label: "账号与人员", value: "管理", note: "维护账号、角色和组织信息。", } : null, roleCapabilities.canManageSystem ? { key: "system-workspace", href: "/system/workspace", label: "工作区设置", value: "配置", note: "检查入口、通知和基础配置。", } : null, roleCapabilities.canManageSystem ? { key: "system-audit", href: "/audit", label: "审计日志", value: "查看", note: "查询关键操作留痕。", } : null, ] as Array).filter(isSummaryItem); const quickActions: QuickActionConfig[] = []; if (roleCapabilities.canApproveTasks) { quickActions.push({ key: "approval", icon: "fact_check", label: "处理审批", description: "待办处理", to: "/workflow/tasks", }); } if (roleCapabilities.canUseFinance && payload.financeSummary) { quickActions.push({ key: "finance", icon: "payments", label: "财务处理", description: "付款 / 风险", to: payload.financeSummary.href, }); } if (roleCapabilities.canManageSystem) { quickActions.push({ key: "system", icon: "admin_panel_settings", label: "系统管理", description: "账号 / 配置", to: "/people", }); } if (!roleCapabilities.isAdminOnly && hasAnyPermission(permissions, LEAVE_PORTAL_PERMISSIONS)) { quickActions.push({ key: "leave", icon: "event_available", label: "提交请假", description: "请假申请", to: "/leave-requests?action=create", }); } if (!roleCapabilities.isAdminOnly && hasAnyPermission(permissions, EXPENSE_PORTAL_PERMISSIONS)) { quickActions.push({ key: "expense", icon: "request_quote", label: "提交报销", description: "报销单据", to: "/reimbursements", }); } quickActions.push({ key: "attendance", icon: "schedule", label: "考勤汇总", description: "企微摘要 / OA 修正", to: "/attendance", }); if (payload.weeklyReport || payload.weeklySummary) { quickActions.push({ key: "weekly", icon: "article", label: "提交周报", description: "填写 / 查看缺口", to: "/weekly-reports", }); } return ( 完成工作区设置 ) : null } />
{primaryCards.map((card) => ( ))}
{showGettingStarted ? ( )} > ) : null}
)} > )} >
{isManagerView ? ( <>
{roleCapabilities.canApproveTasks ? ( ) : null} {roleCapabilities.canApproveTasks ? ( ) : null} {roleCapabilities.canManageSystem ? ( ) : null} {roleCapabilities.canUseFinance ? ( ) : null} {roleCapabilities.canManageSystem ? ( ) : null} {roleCapabilities.canViewTeam ? ( ) : null}
) : (
)}
); }