Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions .github/workflows/content-check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ on:
- "**/*.mdx"
- "source.config.ts"
- "app/docs/**"
- "data/**"
- "tests/**"
- "lib/source.ts"
- "mdx-components.tsx"
- "package.json"
Expand All @@ -26,15 +28,15 @@ jobs:
- uses: actions/checkout@v4

- uses: pnpm/action-setup@v4
with:
version: 10

- uses: actions/setup-node@v4
with:
node-version: 20
cache: "pnpm"

- run: pnpm install --frozen-lockfile
- name: Run tests
run: pnpm test
# Non-blocking image migration + lint (visibility only)
- name: Migrate images next to MDX (check only)
run: pnpm migrate:images || echo "[warn] migrate:images failed (non-blocking)"
Expand Down
2 changes: 0 additions & 2 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@ jobs:
- uses: actions/checkout@v4

- uses: pnpm/action-setup@v4
with:
version: 10

- uses: actions/setup-node@v4
with:
Expand Down
2 changes: 0 additions & 2 deletions .github/workflows/sync-uuid.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,6 @@ jobs:
- uses: actions/checkout@v4

- uses: pnpm/action-setup@v4
with:
version: 9

- uses: actions/setup-node@v4
with:
Expand Down
5 changes: 4 additions & 1 deletion .husky/pre-commit
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,8 @@ git add -A
# 2) 校验图片路径与命名(不合规则阻止提交)
pnpm lint:images || exit 1

# 3) 其余按 lint-staged 处理(如 Prettier)
# 3) 运行 Vitest,确保配置校验通过
pnpm test || exit 1

# 4) 其余按 lint-staged 处理(如 Prettier)
pnpm exec lint-staged
18 changes: 10 additions & 8 deletions app/components/ActivityTicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,19 @@ import Image from "next/image";
import Link from "next/link";
import { useCallback, useEffect, useMemo, useState } from "react";
import { ChevronLeft, ChevronRight } from "lucide-react";
import eventsData from "@/data/event.json";
import type { ActivityEvent, ActivityEventsConfig } from "@/app/types/event";
import {
activityEventsConfig,
type ActivityEvent,
type ActivityTickerSettings,
} from "@/app/types/event";
import { cn } from "@/lib/utils";

const { events: rawEvents, settings } = activityEventsConfig;

const {
events: rawEvents,
settings: {
maxItems: configuredMaxItems = 3,
rotationIntervalMs: configuredRotationIntervalMs = 8000,
},
} = eventsData as ActivityEventsConfig;
maxItems: configuredMaxItems = 3,
rotationIntervalMs: configuredRotationIntervalMs = 8000,
}: ActivityTickerSettings = settings;

// 默认配置,从data/event.json中读取配置
const MAX_ITEMS = configuredMaxItems;
Expand Down
62 changes: 38 additions & 24 deletions app/types/event.ts
Original file line number Diff line number Diff line change
@@ -1,34 +1,48 @@
/**
* @description: 活动横幅所需要的类型
* @param name 活动名称
* @param discord discord活动链接
* @param playback 回放链接
* @param coverUrl 封面地址
* @param deprecated 是否已经结束
*/
export interface ActivityEvent {
import { z } from "zod";
import eventsJson from "@/data/event.json";

export const ActivityEventSchema = z.object({
/** 活动名称,用于轮播标题 */
name: string;
name: z.string().min(1, "name 不能为空"),
/** Discord 活动入口链接 */
discord: string;
discord: z.string().min(1, "discord 入口不能为空"),
/** 活动回放链接,deprecated 为 true 时展示 */
playback?: string;
playback: z.string().min(1, "playback 链接不能为空").optional(),
/** 活动封面,可以是静态资源相对路径或完整 URL */
coverUrl: string;
coverUrl: z.string().min(1, "coverUrl 不能为空"),
/** 是否为已结束活动,true 时展示 Playback 按钮 */
deprecated: boolean;
}
deprecated: z.boolean(),
});

/** 活动轮播可配置参数 */
export interface ActivityTickerSettings {
export const ActivityTickerSettingsSchema = z.object({
/** 首屏最多展示的活动数量 */
maxItems: number;
maxItems: z.number().int().positive("maxItems 需要为正整数"),
/** 自动轮播的间隔时间(毫秒) */
rotationIntervalMs: number;
}
rotationIntervalMs: z
.number()
.int()
.positive("rotationIntervalMs 需要为正整数"),
});

export const ActivityEventsConfigSchema = z.object({
settings: ActivityTickerSettingsSchema,
events: z.array(ActivityEventSchema),
});

/** event.json 的整体结构 */
export interface ActivityEventsConfig {
settings: ActivityTickerSettings;
events: ActivityEvent[];
type ActivityEvent = z.infer<typeof ActivityEventSchema>;
type ActivityTickerSettings = z.infer<typeof ActivityTickerSettingsSchema>;
type ActivityEventsConfig = z.infer<typeof ActivityEventsConfigSchema>;

const parsedEventsConfig = ActivityEventsConfigSchema.safeParse(eventsJson);

if (!parsedEventsConfig.success) {
const issueMessages = parsedEventsConfig.error.issues
.map((issue) => `- ${issue.path.join(".") || "(root)"}: ${issue.message}`)
.join("\n");
throw new Error(`event.json 配置不合法:\n${issueMessages}`);
}

export const activityEventsConfig: ActivityEventsConfig =
parsedEventsConfig.data;

export type { ActivityEvent, ActivityEventsConfig, ActivityTickerSettings };
8 changes: 6 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
"prebuild": "node scripts/escape-angles.mjs",
"build": "next build",
"start": "next start -p 3000",
"test": "vitest run",
"test:watch": "vitest",
"postinstall": "fumadocs-mdx",
"prepare": "husky",
"lint:images": "node scripts/check-images.mjs",
Expand Down Expand Up @@ -90,9 +92,11 @@
"tw-animate-css": "^1.3.8",
"typescript": "^5.9.2",
"typescript-eslint": "^8.46.2",
"vercel": "^48.1.0"
"vercel": "^48.1.0",
"vitest": "^4.0.6"
},
"lint-staged": {
"**/*": "prettier --write --ignore-unknown"
}
},
"packageManager": "pnpm@10.20.0"
}
Loading