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:
@@ -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",
|
||||
},
|
||||
]
|
||||
@@ -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,
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user