feat(web): migrate application shell to TanStack Start

- replace the legacy client-only Vite bootstrap with TanStack Router routes, Start SSR, and Nitro output

- compose the reusable layout, navigation, appearance, chats, and notifications blocks with application-owned data

- persist UI preferences through server functions and initialize theme, sidebar, and breakpoint state before hydration

- add React Query, hotkey, toaster, and responsive dashboard integration

- add application-owned Lingui configuration and catalogs, mount the runtime provider, and lazily enable I18nDevtool in development

- remove superseded HTML, main entry, theme provider, and application ESLint configuration
This commit is contained in:
Maofeng
2026-07-29 21:23:17 +08:00
parent 9e3f1d6b59
commit a9480ad9aa
32 changed files with 1555 additions and 309 deletions
+46
View File
@@ -0,0 +1,46 @@
import type { ChatThread } from "@workspace/blocks/chats"
export const chatThreads: readonly ChatThread[] = [
{
avatarUrl:
"https://pub-c5e31b5cdafb419fb247a8ac2e78df7a.r2.dev/public/assets/images/mock/avatar/avatar-1.webp",
name: "Jayvion Simon",
presence: "offline",
},
{
avatarUrl:
"https://pub-c5e31b5cdafb419fb247a8ac2e78df7a.r2.dev/public/assets/images/mock/avatar/avatar-2.webp",
name: "Lucian Obrien",
presence: "online",
},
{
avatarUrl:
"https://pub-c5e31b5cdafb419fb247a8ac2e78df7a.r2.dev/public/assets/images/mock/avatar/avatar-3.webp",
name: "Deja Brady",
},
{
avatarUrl:
"https://pub-c5e31b5cdafb419fb247a8ac2e78df7a.r2.dev/public/assets/images/mock/avatar/avatar-4.webp",
name: "Harrison Stein",
},
{
avatarUrl:
"https://pub-c5e31b5cdafb419fb247a8ac2e78df7a.r2.dev/public/assets/images/mock/avatar/avatar-5.webp",
name: "Reece Chung",
},
{
avatarUrl:
"https://pub-c5e31b5cdafb419fb247a8ac2e78df7a.r2.dev/public/assets/images/mock/avatar/avatar-6.webp",
name: "Cristopher Cardenas",
},
{
avatarUrl:
"https://pub-c5e31b5cdafb419fb247a8ac2e78df7a.r2.dev/public/assets/images/mock/avatar/avatar-8.webp",
name: "Melanie Noble",
},
{
avatarUrl:
"https://pub-c5e31b5cdafb419fb247a8ac2e78df7a.r2.dev/public/assets/images/mock/avatar/avatar-9.webp",
name: "Chase Day",
},
]
+594
View File
@@ -0,0 +1,594 @@
import {
Alert02Icon,
Analytics01Icon,
AnalyticsUpIcon,
AppWindowIcon,
ArrowDataTransferHorizontalIcon,
BubbleChatIcon,
Calculator01Icon,
CallMissed01Icon,
CancelCircleIcon,
Chart01Icon,
ChartIncreaseIcon,
CheckmarkBadge01Icon,
CheckmarkCircle01Icon,
ClipboardListIcon,
Clock01Icon,
CodeSquareIcon,
Compass01Icon,
CreditCardIcon,
CrownIcon,
Delete02Icon,
DeliveryTruck01Icon,
Discount01Icon,
File01Icon,
GalleryHorizontalIcon,
GiftIcon,
HistoryIcon,
IdentityCardIcon,
InboxDownloadIcon,
InboxUploadIcon,
Invoice03Icon,
Key01Icon,
LinkCircleIcon,
Location01Icon,
LockKeyIcon,
Medal01Icon,
MoneySendSquareIcon,
Note01Icon,
Package02Icon,
PackageIcon,
Refresh01Icon,
RoadLocation01Icon,
SecurityWarningIcon,
Settings01Icon,
Settings03Icon,
Share01Icon,
ShoppingBag01Icon,
ShoppingCart01Icon,
StarCircleIcon,
StarIcon,
Store01Icon,
Structure01Icon,
Task01Icon,
Ticket01Icon,
UserGroupIcon,
UserShield01Icon,
Wallet01Icon,
WalletCardsIcon,
} from "@hugeicons/core-free-icons"
import type { NavigationGroup } from "@workspace/blocks/navigation"
export const primaryNavigationGroups: readonly NavigationGroup[] = [
{
id: "overview",
label: "概览",
items: [
{
id: "overview-dashboard",
to: "/",
label: "经营总览",
icon: Analytics01Icon,
},
{
id: "overview-realtime",
to: "/analytics/realtime",
label: "实时监控",
icon: AnalyticsUpIcon,
},
{
id: "overview-tasks",
to: "/workspace/tasks",
label: "待办中心",
icon: Task01Icon,
},
],
},
{
id: "catalog-and-inventory",
label: "商品与库存",
items: [
{
id: "catalog-products",
label: "商品中心",
icon: ShoppingBag01Icon,
items: [
{
id: "catalog-products-list",
to: "/catalog/products/list",
label: "商品列表",
icon: ShoppingBag01Icon,
},
{
id: "catalog-products-drafts",
to: "/catalog/products/drafts",
label: "商品草稿",
icon: File01Icon,
},
{
id: "catalog-products-bundles",
to: "/catalog/products/bundles",
label: "组合商品",
icon: Package02Icon,
},
{
id: "catalog-products-attributes",
label: "商品属性",
icon: Settings03Icon,
items: [
{
id: "catalog-product-specifications",
to: "/catalog/products/attributes/specifications",
label: "规格模板",
icon: ClipboardListIcon,
},
{
id: "catalog-product-brands",
to: "/catalog/products/attributes/brands",
label: "品牌资料",
icon: StarIcon,
},
],
},
],
},
{
id: "catalog-categories",
to: "/catalog/categories",
label: "分类目录",
icon: ClipboardListIcon,
items: [
{
id: "catalog-category-tree",
to: "/catalog/categories/tree",
label: "分类树",
icon: Structure01Icon,
},
{
id: "catalog-category-navigation",
to: "/catalog/categories/navigation",
label: "导航分类",
icon: Compass01Icon,
},
],
},
{
id: "inventory-center",
to: "/inventory",
label: "库存中心",
icon: PackageIcon,
items: [
{
id: "inventory-overview",
to: "/inventory/overview",
label: "库存总览",
icon: Chart01Icon,
},
{
id: "inventory-inbound",
to: "/inventory/inbound",
label: "入库单",
icon: InboxDownloadIcon,
},
{
id: "inventory-outbound",
to: "/inventory/outbound",
label: "出库单",
icon: InboxUploadIcon,
},
{
id: "inventory-transfers",
to: "/inventory/transfers",
label: "调拨单",
icon: ArrowDataTransferHorizontalIcon,
},
{
id: "inventory-alerts",
to: "/inventory/alerts",
label: "库存预警",
icon: Alert02Icon,
},
],
},
{
id: "content-assets",
to: "/content",
label: "内容素材",
icon: File01Icon,
items: [
{
id: "content-articles",
to: "/content/articles",
label: "内容文章",
icon: File01Icon,
},
{
id: "content-media",
to: "/content/media",
label: "媒体资源",
icon: GalleryHorizontalIcon,
},
{
id: "content-pages",
to: "/content/pages",
label: "自定义页面",
icon: AppWindowIcon,
},
],
},
],
},
{
id: "trade-and-fulfillment",
label: "交易与履约",
items: [
{
id: "trade-orders",
to: "/trade/orders",
label: "订单中心",
icon: ShoppingCart01Icon,
items: [
{
id: "trade-orders-awaiting-payment",
to: "/trade/orders/awaiting-payment",
label: "待付款订单",
icon: WalletCardsIcon,
},
{
id: "trade-orders-awaiting-shipment",
to: "/trade/orders/awaiting-shipment",
label: "待发货订单",
icon: PackageIcon,
},
{
id: "trade-orders-shipped",
to: "/trade/orders/shipped",
label: "配送中订单",
icon: DeliveryTruck01Icon,
},
{
id: "trade-orders-completed",
to: "/trade/orders/completed",
label: "已完成订单",
icon: CheckmarkCircle01Icon,
},
{
id: "trade-orders-canceled",
to: "/trade/orders/canceled",
label: "已取消订单",
icon: CancelCircleIcon,
},
],
},
{
id: "fulfillment-center",
to: "/fulfillment",
label: "配送履约",
icon: DeliveryTruck01Icon,
items: [
{
id: "fulfillment-deliveries",
to: "/fulfillment/deliveries",
label: "配送任务",
icon: Location01Icon,
},
{
id: "fulfillment-pickups",
to: "/fulfillment/pickups",
label: "自提核销",
icon: Store01Icon,
},
{
id: "fulfillment-freight-rules",
to: "/fulfillment/freight-rules",
label: "运费规则",
icon: RoadLocation01Icon,
},
],
},
{
id: "after-sales-center",
to: "/after-sales",
label: "售后中心",
icon: CallMissed01Icon,
items: [
{
id: "after-sales-returns",
to: "/after-sales/returns",
label: "退货申请",
icon: Refresh01Icon,
},
{
id: "after-sales-refunds",
to: "/after-sales/refunds",
label: "退款审核",
icon: MoneySendSquareIcon,
},
{
id: "after-sales-disputes",
to: "/after-sales/disputes",
label: "交易纠纷",
icon: SecurityWarningIcon,
},
],
},
],
},
{
id: "customers-and-marketing",
label: "客户与营销",
items: [
{
id: "customer-center",
to: "/customers",
label: "客户中心",
icon: IdentityCardIcon,
items: [
{
id: "customer-profiles",
to: "/customers/profiles",
label: "客户档案",
icon: IdentityCardIcon,
},
{
id: "customer-segments",
to: "/customers/segments",
label: "客户分群",
icon: UserGroupIcon,
},
{
id: "customer-tiers",
to: "/customers/tiers",
label: "客户等级",
icon: CrownIcon,
},
{
id: "customer-risk-list",
to: "/customers/risk-list",
label: "风险名单",
icon: SecurityWarningIcon,
},
],
},
{
id: "marketing-campaigns",
to: "/marketing/campaigns",
label: "营销活动",
icon: Discount01Icon,
items: [
{
id: "marketing-coupons",
to: "/marketing/coupons",
label: "优惠券管理",
icon: Ticket01Icon,
},
{
id: "marketing-flash-sales",
to: "/marketing/flash-sales",
label: "限时折扣",
icon: Clock01Icon,
},
{
id: "marketing-gift-promotions",
to: "/marketing/gift-promotions",
label: "满赠活动",
icon: GiftIcon,
},
{
id: "marketing-referrals",
to: "/marketing/referrals",
label: "邀请有礼",
icon: Share01Icon,
},
],
},
{
id: "loyalty-center",
to: "/loyalty",
label: "会员权益",
icon: Medal01Icon,
items: [
{
id: "loyalty-points",
to: "/loyalty/points",
label: "积分规则",
icon: StarCircleIcon,
},
{
id: "loyalty-stored-value",
to: "/loyalty/stored-value",
label: "储值账户",
icon: Wallet01Icon,
},
{
id: "loyalty-benefits",
to: "/loyalty/benefits",
label: "权益配置",
icon: CheckmarkBadge01Icon,
},
],
},
],
},
{
id: "data-and-finance",
label: "数据与财务",
items: [
{
id: "report-center",
to: "/reports",
label: "数据报表",
icon: Chart01Icon,
items: [
{
id: "report-sales",
to: "/reports/sales",
label: "销售分析",
icon: AnalyticsUpIcon,
},
{
id: "report-products",
to: "/reports/products",
label: "商品分析",
icon: ShoppingBag01Icon,
},
{
id: "report-customers",
to: "/reports/customers",
label: "客群分析",
icon: UserGroupIcon,
},
{
id: "report-channels",
to: "/reports/channels",
label: "渠道分析",
icon: ChartIncreaseIcon,
},
],
},
{
id: "finance-center",
to: "/finance",
label: "财务中心",
icon: WalletCardsIcon,
items: [
{
id: "finance-settlements",
to: "/finance/settlements",
label: "结算账单",
icon: Invoice03Icon,
},
{
id: "finance-reconciliation",
to: "/finance/reconciliation",
label: "对账管理",
icon: Calculator01Icon,
},
{
id: "finance-invoices",
to: "/finance/invoices",
label: "发票管理",
icon: File01Icon,
},
{
id: "finance-payouts",
to: "/finance/payouts",
label: "打款记录",
icon: MoneySendSquareIcon,
},
],
},
],
},
{
id: "system-management",
label: "系统管理",
items: [
{
id: "system-members",
to: "/system/members",
label: "组织成员",
icon: UserShield01Icon,
items: [
{
id: "system-members-admins",
to: "/system/members/admins",
label: "管理员账号",
icon: UserShield01Icon,
},
{
id: "system-members-roles",
to: "/system/members/roles",
label: "角色管理",
icon: UserGroupIcon,
},
{
id: "system-members-permissions",
to: "/system/members/permissions",
label: "权限策略",
icon: LockKeyIcon,
},
],
},
{
id: "system-notifications",
to: "/system/notifications",
label: "消息中心",
icon: BubbleChatIcon,
items: [
{
id: "system-notification-templates",
to: "/system/notifications/templates",
label: "通知模板",
icon: File01Icon,
},
{
id: "system-notification-logs",
to: "/system/notifications/logs",
label: "发送记录",
icon: HistoryIcon,
},
],
},
{
id: "system-audit",
to: "/system/audit",
label: "操作审计",
icon: Note01Icon,
},
{
id: "system-integrations",
to: "/system/integrations",
label: "开放集成",
icon: CodeSquareIcon,
items: [
{
id: "system-integration-api-keys",
to: "/system/integrations/api-keys",
label: "API 密钥",
icon: Key01Icon,
},
{
id: "system-integration-webhooks",
to: "/system/integrations/webhooks",
label: "Webhook 配置",
icon: LinkCircleIcon,
},
],
},
{
id: "system-settings",
to: "/system-configs",
label: "系统配置",
icon: Settings01Icon,
items: [
{
id: "system-settings-store",
to: "/system-configs/store",
label: "门店资料",
icon: Store01Icon,
},
{
id: "system-settings-payments",
to: "/system-configs/payments",
label: "支付渠道",
icon: CreditCardIcon,
},
{
id: "system-settings-shipping",
to: "/system-configs/shipping",
label: "配送设置",
icon: DeliveryTruck01Icon,
},
],
},
{
id: "system-recycle-bin",
to: "/system/recycle-bin",
label: "回收站",
icon: Delete02Icon,
},
],
},
]
+226
View File
@@ -0,0 +1,226 @@
import {
DeliveryTruck01Icon,
Mail01Icon,
Message01Icon,
PackageIcon,
} from "@hugeicons/core-free-icons"
import type { QueryFunctionContext } from "@tanstack/react-query"
import type { Notification } from "@workspace/blocks/notifications"
const RANDOM_USER_API_URL =
"https://randomuser.me/api/1.4/?results=4&inc=name,email,picture&nat=au,ca,de,fr,gb,nl,nz,us&noinfo"
interface RandomUser {
email: string
name: {
first: string
last: string
}
picture: {
medium: string
}
}
const HOUR_IN_MILLISECONDS = 60 * 60 * 1000
function getCreatedAt(hoursAgo: number) {
return new Date(Date.now() - hoursAgo * HOUR_IN_MILLISECONDS).toISOString()
}
export const fallbackNotifications: readonly Notification[] = [
{
id: "friend-request-deja-brady",
title: "Deja Brady sent you a friend request",
createdAt: getCreatedAt(11),
category: "Communication",
isRead: false,
media: {
kind: "avatar",
src: "https://pub-c5e31b5cdafb419fb247a8ac2e78df7a.r2.dev/public/assets/images/mock/avatar/avatar-2.webp",
alt: "Deja Brady",
},
actions: [
{ id: "accept", label: "Accept" },
{ id: "decline", label: "Decline", intent: "neutral" },
],
},
{
id: "mention-minimal-ui",
title: "Jayvon Hull mentioned you in Minimal UI",
createdAt: getCreatedAt(24),
category: "Project UI",
to: "/workspace/tasks",
isRead: false,
media: {
kind: "avatar",
src: "https://pub-c5e31b5cdafb419fb247a8ac2e78df7a.r2.dev/public/assets/images/mock/avatar/avatar-3.webp",
alt: "Jayvon Hull",
},
actions: [{ id: "view", label: "View", intent: "neutral" }],
},
{
id: "order-placed",
title: "Your order is placed",
description: "The order is confirmed and waiting for shipping.",
createdAt: getCreatedAt(24 * 6),
category: "Order",
isRead: true,
media: {
kind: "icon",
icon: PackageIcon,
tone: "warning",
},
},
{
id: "order-shipping",
title: "Delivery in progress",
description: "Your order has left the warehouse and is being shipped.",
createdAt: getCreatedAt(24 * 7),
category: "Order",
isRead: true,
media: {
kind: "icon",
icon: DeliveryTruck01Icon,
tone: "success",
},
},
{
id: "unread-messages",
title: "You have 5 unread messages",
createdAt: getCreatedAt(24 * 8),
category: "Communication",
to: "/workspace/tasks",
isRead: true,
media: {
kind: "icon",
icon: Message01Icon,
tone: "info",
},
},
{
id: "new-mail",
title: "You have new mail",
createdAt: getCreatedAt(24 * 9),
category: "Communication",
isRead: true,
media: {
kind: "icon",
icon: Mail01Icon,
tone: "destructive",
},
},
]
export const notificationsQueryKey = ["notifications"] as const
function isRecord(value: unknown): value is Record<string, unknown> {
return typeof value === "object" && value !== null
}
function isRandomUser(value: unknown): value is RandomUser {
if (!isRecord(value) || !isRecord(value.name) || !isRecord(value.picture)) {
return false
}
return (
typeof value.email === "string" &&
typeof value.name.first === "string" &&
typeof value.name.last === "string" &&
typeof value.picture.medium === "string"
)
}
function getUserName(user: RandomUser) {
return `${user.name.first} ${user.name.last}`
}
function getAvatarMedia(user: RandomUser) {
return {
alt: getUserName(user),
kind: "avatar" as const,
src: user.picture.medium,
}
}
function createRandomUserNotifications(
users: readonly RandomUser[]
): readonly Notification[] {
const [friend, mention, commenter, collaborator] = users
if (!friend || !mention || !commenter || !collaborator) {
throw new Error("Random User API returned too few users")
}
return [
{
id: `friend-request:${friend.email}`,
title: `${getUserName(friend)} sent you a friend request`,
createdAt: getCreatedAt(11),
category: "Communication",
isRead: false,
media: getAvatarMedia(friend),
actions: [
{ id: "accept", label: "Accept" },
{ id: "decline", label: "Decline", intent: "neutral" },
],
},
{
id: `mention:${mention.email}`,
title: `${getUserName(mention)} mentioned you in Minimal UI`,
createdAt: getCreatedAt(24),
category: "Project UI",
to: "/workspace/tasks",
isRead: false,
media: getAvatarMedia(mention),
actions: [{ id: "view", label: "View", intent: "neutral" }],
},
{
id: `comment:${commenter.email}`,
title: `${getUserName(commenter)} commented on your task`,
description: "Can you review the latest changes?",
createdAt: getCreatedAt(24 * 2),
category: "Project UI",
to: "/workspace/tasks",
isRead: false,
media: getAvatarMedia(commenter),
},
{
id: `collaborator:${collaborator.email}`,
title: `${getUserName(collaborator)} joined your workspace`,
createdAt: getCreatedAt(24 * 4),
category: "Team",
isRead: true,
media: getAvatarMedia(collaborator),
},
...fallbackNotifications.slice(2),
]
}
export async function fetchMockNotifications({
signal,
}: QueryFunctionContext): Promise<readonly Notification[]> {
try {
const response = await fetch(RANDOM_USER_API_URL, { signal })
if (!response.ok) {
throw new Error(`Random User API responded with ${response.status}`)
}
const payload: unknown = await response.json()
if (!isRecord(payload) || !Array.isArray(payload.results)) {
throw new Error("Random User API returned an invalid response")
}
const users = payload.results.filter(isRandomUser)
return createRandomUserNotifications(users)
} catch (error) {
if (signal.aborted) {
throw error
}
return fallbackNotifications
}
}