diff --git a/backend/src/module/api/bangumi.py b/backend/src/module/api/bangumi.py index 3c2aa7d3..bf6e577f 100644 --- a/backend/src/module/api/bangumi.py +++ b/backend/src/module/api/bangumi.py @@ -41,6 +41,10 @@ class OffsetSuggestionDetail(BaseModel): confidence: Literal["high", "medium", "low"] +class SetWeekdayRequest(BaseModel): + weekday: Optional[int] = None # 0-6 for Mon-Sun, None to reset + + class DetectOffsetRequest(BaseModel): """Request body for detect-offset endpoint.""" title: str @@ -339,3 +343,41 @@ async def get_needs_review(): """Get all bangumi that need review for offset mismatch.""" with Database() as db: return db.bangumi.get_needs_review() + + +@router.patch( + path="/{bangumi_id}/weekday", + response_model=APIResponse, + dependencies=[Depends(get_current_user)], +) +async def set_weekday(bangumi_id: int, request: SetWeekdayRequest): + """Manually set the broadcast weekday for a bangumi.""" + if request.weekday is not None and not (0 <= request.weekday <= 6): + return JSONResponse( + status_code=400, + content={ + "status": False, + "msg_en": "Weekday must be 0-6 (Mon-Sun) or null.", + "msg_zh": "星期必须是 0-6(周一至周日)或空。", + }, + ) + with Database() as db: + success = db.bangumi.set_weekday(bangumi_id, request.weekday) + if success: + action = f"weekday {request.weekday}" if request.weekday is not None else "unknown" + return JSONResponse( + status_code=200, + content={ + "status": True, + "msg_en": f"Set bangumi to {action}.", + "msg_zh": f"已设置放送日为 {action}。", + }, + ) + return JSONResponse( + status_code=404, + content={ + "status": False, + "msg_en": f"Bangumi {bangumi_id} not found.", + "msg_zh": f"未找到番剧 {bangumi_id}。", + }, + ) diff --git a/backend/src/module/database/bangumi.py b/backend/src/module/database/bangumi.py index fe41029d..b230d62c 100644 --- a/backend/src/module/database/bangumi.py +++ b/backend/src/module/database/bangumi.py @@ -657,3 +657,25 @@ class BangumiDatabase: _invalidate_bangumi_cache() logger.debug("[Database] Cleared needs_review for bangumi id %s", _id) return True + + def set_weekday(self, _id: int, weekday: int | None) -> bool: + """Set air_weekday and weekday_locked for manual calendar assignment.""" + bangumi = self.session.get(Bangumi, _id) + if not bangumi: + return False + if weekday is not None: + bangumi.air_weekday = weekday + bangumi.weekday_locked = True + else: + bangumi.air_weekday = None + bangumi.weekday_locked = False + self.session.add(bangumi) + self.session.commit() + _invalidate_bangumi_cache() + logger.debug( + "[Database] Set weekday=%s, locked=%s for bangumi id %s", + weekday, + bangumi.weekday_locked, + _id, + ) + return True diff --git a/backend/src/module/database/combine.py b/backend/src/module/database/combine.py index b69889f5..66afa058 100644 --- a/backend/src/module/database/combine.py +++ b/backend/src/module/database/combine.py @@ -23,7 +23,7 @@ logger = logging.getLogger(__name__) TABLE_MODELS: list[type[SQLModel]] = [Bangumi, RSSItem, Torrent, User, Passkey] # Increment this when adding new migrations to MIGRATIONS list. -CURRENT_SCHEMA_VERSION = 8 +CURRENT_SCHEMA_VERSION = 9 # Each migration is a tuple of (version, description, list of SQL statements). # Migrations are applied in order. A migration at index i brings the schema @@ -103,6 +103,13 @@ MIGRATIONS = [ "ALTER TABLE bangumi ADD COLUMN title_aliases TEXT DEFAULT NULL", ], ), + ( + 9, + "add weekday_locked column to bangumi", + [ + "ALTER TABLE bangumi ADD COLUMN weekday_locked BOOLEAN DEFAULT 0", + ], + ), ] @@ -198,6 +205,10 @@ class Database(Session): columns = [col["name"] for col in inspector.get_columns("bangumi")] if "title_aliases" in columns: needs_run = False + if "bangumi" in tables and version == 9: + columns = [col["name"] for col in inspector.get_columns("bangumi")] + if "weekday_locked" in columns: + needs_run = False if needs_run: try: with self.engine.connect() as conn: diff --git a/backend/src/module/manager/torrent.py b/backend/src/module/manager/torrent.py index 79ac9526..1bd28f94 100644 --- a/backend/src/module/manager/torrent.py +++ b/backend/src/module/manager/torrent.py @@ -210,7 +210,7 @@ class TorrentManager(Database): bangumis = self.bangumi.search_all() updated = 0 for bangumi in bangumis: - if bangumi.deleted: + if bangumi.deleted or bangumi.weekday_locked: continue weekday = match_weekday( bangumi.official_title, bangumi.title_raw, calendar_items diff --git a/backend/src/module/models/bangumi.py b/backend/src/module/models/bangumi.py index e2110180..c3b303bd 100644 --- a/backend/src/module/models/bangumi.py +++ b/backend/src/module/models/bangumi.py @@ -36,6 +36,9 @@ class Bangumi(SQLModel, table=True): air_weekday: Optional[int] = Field( default=None, alias="air_weekday", title="放送星期" ) + weekday_locked: bool = Field( + default=False, alias="weekday_locked", title="放送星期锁定" + ) needs_review: bool = Field(default=False, alias="needs_review", title="需要检查") needs_review_reason: Optional[str] = Field( default=None, alias="needs_review_reason", title="检查原因" @@ -77,6 +80,9 @@ class BangumiUpdate(SQLModel): air_weekday: Optional[int] = Field( default=None, alias="air_weekday", title="放送星期" ) + weekday_locked: bool = Field( + default=False, alias="weekday_locked", title="放送星期锁定" + ) needs_review: bool = Field(default=False, alias="needs_review", title="需要检查") needs_review_reason: Optional[str] = Field( default=None, alias="needs_review_reason", title="检查原因" diff --git a/docs/plans/2026-02-23-calendar-drag-organize-design.md b/docs/plans/2026-02-23-calendar-drag-organize-design.md new file mode 100644 index 00000000..ea1b414a --- /dev/null +++ b/docs/plans/2026-02-23-calendar-drag-organize-design.md @@ -0,0 +1,406 @@ +# Calendar Drag-to-Organize Unknown Bangumi — Implementation Plan + +> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. + +**Goal:** Allow users to drag bangumi cards from the "Unknown" section into weekday columns in the calendar view, with a locked flag to prevent calendar refresh from overwriting manual assignments. + +**Architecture:** Add a `weekday_locked` boolean field to the Bangumi model. A new API endpoint sets weekday + locks it. The `refresh_calendar()` method skips locked items. Frontend uses vuedraggable for smooth drag-and-drop from Unknown to weekday columns, with a reset/unlock button on manually-pinned cards. + +**Tech Stack:** Python/FastAPI (backend), SQLModel/SQLite (data), Vue 3 + TypeScript + vuedraggable (frontend) + +--- + +### Task 1: Add `weekday_locked` field to data model + migration + +**Files:** +- Modify: `backend/src/module/models/bangumi.py:36-38` +- Modify: `backend/src/module/database/combine.py:26,99-105` + +**Step 1: Add `weekday_locked` field to `Bangumi` model** + +In `backend/src/module/models/bangumi.py`, after the `air_weekday` field (line 38), add: + +```python +weekday_locked: bool = Field( + default=False, alias="weekday_locked", title="放送星期锁定" +) +``` + +**Step 2: Add `weekday_locked` to `BangumiUpdate` model** + +In the same file, after `air_weekday` in `BangumiUpdate` (line 79), add: + +```python +weekday_locked: bool = Field( + default=False, alias="weekday_locked", title="放送星期锁定" +) +``` + +**Step 3: Add database migration** + +In `backend/src/module/database/combine.py`: + +1. Increment `CURRENT_SCHEMA_VERSION` from `8` to `9` +2. Add migration entry after the existing migration 8: + +```python +( + 9, + "add weekday_locked column to bangumi", + [ + "ALTER TABLE bangumi ADD COLUMN weekday_locked BOOLEAN DEFAULT 0", + ], +), +``` + +3. Add skip-check in `run_migrations()` after the version 8 check: + +```python +if "bangumi" in tables and version == 9: + columns = [col["name"] for col in inspector.get_columns("bangumi")] + if "weekday_locked" in columns: + needs_run = False +``` + +**Step 4: Run backend tests to verify migration** + +Run: `cd backend && uv run pytest src/test/ -v -k "not test_mcp"` +Expected: All pass (new column has default, so no breaking changes) + +**Step 5: Commit** + +```bash +git add backend/src/module/models/bangumi.py backend/src/module/database/combine.py +git commit -m "feat(model): add weekday_locked field to bangumi for manual calendar assignment" +``` + +--- + +### Task 2: Add backend API endpoint for setting weekday + +**Files:** +- Modify: `backend/src/module/api/bangumi.py` +- Modify: `backend/src/module/database/bangumi.py` + +**Step 1: Add `set_weekday` database method** + +In `backend/src/module/database/bangumi.py`, add method to `BangumiDatabase`: + +```python +def set_weekday(self, _id: int, weekday: int | None) -> bool: + """Set air_weekday and weekday_locked for manual calendar assignment.""" + bangumi = self.session.get(Bangumi, _id) + if not bangumi: + return False + if weekday is not None: + bangumi.air_weekday = weekday + bangumi.weekday_locked = True + else: + bangumi.air_weekday = None + bangumi.weekday_locked = False + self.session.add(bangumi) + self.session.commit() + _invalidate_bangumi_cache() + logger.debug( + "[Database] Set weekday=%s, locked=%s for bangumi id %s", + weekday, + bangumi.weekday_locked, + _id, + ) + return True +``` + +**Step 2: Add API endpoint** + +In `backend/src/module/api/bangumi.py`, add request model and endpoint: + +```python +class SetWeekdayRequest(BaseModel): + weekday: Optional[int] = None # 0-6 for Mon-Sun, None to reset + +@router.patch( + path="/{bangumi_id}/weekday", + response_model=APIResponse, + dependencies=[Depends(get_current_user)], +) +async def set_weekday(bangumi_id: int, request: SetWeekdayRequest): + """Manually set the broadcast weekday for a bangumi.""" + if request.weekday is not None and not (0 <= request.weekday <= 6): + return JSONResponse( + status_code=400, + content={ + "status": False, + "msg_en": "Weekday must be 0-6 (Mon-Sun) or null.", + "msg_zh": "星期必须是 0-6(周一至周日)或空。", + }, + ) + with Database() as db: + success = db.bangumi.set_weekday(bangumi_id, request.weekday) + if success: + action = f"weekday {request.weekday}" if request.weekday is not None else "unknown" + return JSONResponse( + status_code=200, + content={ + "status": True, + "msg_en": f"Set bangumi to {action}.", + "msg_zh": f"已设置放送日为 {action}。", + }, + ) + return JSONResponse( + status_code=404, + content={ + "status": False, + "msg_en": f"Bangumi {bangumi_id} not found.", + "msg_zh": f"未找到番剧 {bangumi_id}。", + }, + ) +``` + +**Step 3: Modify `refresh_calendar()` to skip locked items** + +In `backend/src/module/manager/torrent.py`, in `refresh_calendar()` method (line 212-213), change: + +```python +# Before: +if bangumi.deleted: + continue + +# After: +if bangumi.deleted or bangumi.weekday_locked: + continue +``` + +**Step 4: Run tests** + +Run: `cd backend && uv run pytest src/test/ -v -k "not test_mcp"` +Expected: All pass + +**Step 5: Commit** + +```bash +git add backend/src/module/api/bangumi.py backend/src/module/database/bangumi.py backend/src/module/manager/torrent.py +git commit -m "feat(api): add PATCH /bangumi/{id}/weekday endpoint and skip locked items in calendar refresh" +``` + +--- + +### Task 3: Add frontend TypeScript types and API client + +**Files:** +- Modify: `webui/types/bangumi.ts` +- Modify: `webui/src/api/bangumi.ts` + +**Step 1: Add `weekday_locked` to TypeScript types** + +In `webui/types/bangumi.ts`, add to `BangumiRule` interface (after `air_weekday` line 26): + +```typescript +weekday_locked: boolean; +``` + +Add to `ruleTemplate` (after `air_weekday: null` line 65): + +```typescript +weekday_locked: false, +``` + +**Step 2: Add API method for setting weekday** + +In `webui/src/api/bangumi.ts`, add method to `apiBangumi`: + +```typescript +/** + * 手动设置番剧的放送星期 + * @param bangumiId - bangumi 的 id + * @param weekday - 0-6 for Mon-Sun, null to reset + */ +async setWeekday(bangumiId: number, weekday: number | null) { + const { data } = await axios.patch( + `api/v1/bangumi/${bangumiId}/weekday`, + { weekday } + ); + return data; +}, +``` + +**Step 3: Commit** + +```bash +git add webui/types/bangumi.ts webui/src/api/bangumi.ts +git commit -m "feat(webui): add weekday_locked type and setWeekday API client" +``` + +--- + +### Task 4: Install vuedraggable + +**Files:** +- Modify: `webui/package.json` + +**Step 1: Install vuedraggable** + +```bash +cd webui && pnpm add vuedraggable@next +``` + +**Step 2: Commit** + +```bash +git add webui/package.json webui/pnpm-lock.yaml +git commit -m "chore(webui): add vuedraggable dependency for calendar drag-and-drop" +``` + +--- + +### Task 5: Add i18n strings for drag-and-drop + +**Files:** +- Modify: `webui/src/i18n/en.json` +- Modify: `webui/src/i18n/zh-CN.json` + +**Step 1: Add English i18n strings** + +In `webui/src/i18n/en.json`, inside the `"calendar"` object (before the closing `}`), add: + +```json +"drag_hint": "Drag to assign weekday", +"pinned": "Manually assigned", +"unpin": "Reset to unknown", +"drop_here": "Drop here" +``` + +**Step 2: Add Chinese i18n strings** + +In `webui/src/i18n/zh-CN.json`, inside the `"calendar"` object, add: + +```json +"drag_hint": "拖拽以设置放送日", +"pinned": "手动设置", +"unpin": "重置为未知", +"drop_here": "拖放到此处" +``` + +**Step 3: Commit** + +```bash +git add webui/src/i18n/en.json webui/src/i18n/zh-CN.json +git commit -m "feat(i18n): add calendar drag-and-drop strings" +``` + +--- + +### Task 6: Implement drag-and-drop in calendar.vue (Desktop) + +**Files:** +- Modify: `webui/src/pages/index/calendar.vue` + +This is the main implementation task. The calendar.vue file needs: + +1. **Import vuedraggable** and add drag-and-drop functionality +2. **Wrap Unknown section cards** in a `` component as the drag source +3. **Wrap each weekday column** in a `` component as drop targets +4. **Handle the `onChange` event** to call the API when a card is dropped +5. **Add reset/unpin button** on cards with `weekday_locked === true` +6. **Add visual pin indicator** on locked cards +7. **Add drop-zone highlighting** CSS for when dragging over a weekday column + +Key implementation details: + +- vuedraggable uses `group` option to allow cross-list dragging +- Unknown list: `group: { name: 'calendar', pull: true, put: false }` (can pull from, cannot put into) +- Weekday lists: `group: { name: 'calendar', pull: false, put: true }` (can put into, cannot pull from) +- On `change` event with `added` property, extract the bangumi group's primary ID and target day index, then call `apiBangumi.setWeekday(id, dayIndex)` +- After API success, update the local bangumi store to reflect the change +- The reset button calls `apiBangumi.setWeekday(id, null)` and refreshes store + +CSS additions: +- `.calendar-column--drop-active`: highlight border when dragging over +- `.calendar-card--pinned`: pin icon overlay +- `.calendar-unpin-btn`: reset button style +- `.sortable-ghost`: semi-transparent placeholder during drag +- `.sortable-drag`: shadow on the card being dragged + +**Step 1: Implement the full calendar.vue changes** + +(See implementation — this is a substantial template + script change) + +**Step 2: Test manually in dev server** + +```bash +cd webui && pnpm dev +``` + +Verify: +- Unknown cards can be dragged to weekday columns +- Weekday column highlights on dragover +- Dropped cards show pin icon +- Pin icon has working reset button +- Cards with weekday_locked show pin in weekday columns +- Mobile view still works (no drag on mobile — touch has different UX) + +**Step 3: Commit** + +```bash +git add webui/src/pages/index/calendar.vue +git commit -m "feat(calendar): add drag-and-drop from Unknown to weekday columns with pin/reset" +``` + +--- + +### Task 7: Update bangumi store to handle weekday_locked + +**Files:** +- Modify: `webui/src/store/bangumi.ts` (if needed for reactive updates after setWeekday) + +**Step 1: Add `setWeekday` action to store** + +Add a store action that calls the API and updates the local bangumi array reactively: + +```typescript +async function setWeekday(bangumiId: number, weekday: number | null) { + await apiBangumi.setWeekday(bangumiId, weekday); + // Update local state + const item = bangumi.value?.find((b) => b.id === bangumiId); + if (item) { + item.air_weekday = weekday; + item.weekday_locked = weekday !== null; + } +} +``` + +**Step 2: Commit** + +```bash +git add webui/src/store/bangumi.ts +git commit -m "feat(store): add setWeekday action for calendar drag-and-drop" +``` + +--- + +### Task 8: Final integration test and type check + +**Step 1: Run type check** + +```bash +cd webui && pnpm test:build +``` + +**Step 2: Run backend tests** + +```bash +cd backend && uv run pytest src/test/ -v -k "not test_mcp" +``` + +**Step 3: Run lint** + +```bash +cd webui && pnpm lint +cd backend && uv run ruff check src +``` + +**Step 4: Fix any issues and commit** + +```bash +git add -A +git commit -m "fix: resolve type and lint issues from calendar drag-and-drop feature" +``` diff --git a/webui/package.json b/webui/package.json index 3400d35e..c721d817 100644 --- a/webui/package.json +++ b/webui/package.json @@ -29,11 +29,11 @@ "vue": "^3.5.8", "vue-i18n": "^9.14.0", "vue-inline-svg": "^3.1.4", - "vue-router": "^4.4.5" + "vue-router": "^4.4.5", + "vuedraggable": "^4.1.0" }, "devDependencies": { "@antfu/eslint-config": "^0.38.6", - "@vue/test-utils": "^2.4.6", "@icon-park/vue-next": "^1.4.2", "@intlify/unplugin-vue-i18n": "^0.11.0", "@storybook/addon-essentials": "^7.6.20", @@ -50,9 +50,11 @@ "@vitejs/plugin-vue": "^4.6.2", "@vitejs/plugin-vue-jsx": "^3.1.0", "@vue/runtime-dom": "^3.5.8", + "@vue/test-utils": "^2.4.6", "eslint": "^8.57.1", "eslint-config-prettier": "^8.10.0", "eslint-plugin-storybook": "^0.6.15", + "happy-dom": "^12.10.3", "husky": "^8.0.3", "prettier": "^2.8.8", "radash": "^12.1.0", @@ -66,7 +68,6 @@ "vite": "^4.5.5", "vite-plugin-pwa": "^0.16.7", "vitest": "^0.30.1", - "happy-dom": "^12.10.3", "vue-tsc": "^1.8.27" } } diff --git a/webui/pnpm-lock.yaml b/webui/pnpm-lock.yaml index ab64f2a9..dd5860d0 100644 --- a/webui/pnpm-lock.yaml +++ b/webui/pnpm-lock.yaml @@ -38,6 +38,9 @@ importers: vue-router: specifier: ^4.4.5 version: 4.4.5(vue@3.5.8(typescript@4.9.5)) + vuedraggable: + specifier: ^4.1.0 + version: 4.1.0(vue@3.5.8(typescript@4.9.5)) devDependencies: '@antfu/eslint-config': specifier: ^0.38.6 @@ -5170,6 +5173,9 @@ packages: resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} engines: {node: '>=8'} + sortablejs@1.14.0: + resolution: {integrity: sha512-pBXvQCs5/33fdN1/39pPL0NZF20LeRbLQ5jtnheIPN9JQAaufGjKdWduZn4U7wCtVuzKhmRkI0DFYHYRbB2H1w==} + source-map-js@1.2.1: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} @@ -5791,8 +5797,8 @@ packages: vue-component-type-helpers@2.1.6: resolution: {integrity: sha512-ng11B8B/ZADUMMOsRbqv0arc442q7lifSubD0v8oDXIFoMg/mXwAPUunrroIDkY+mcD0dHKccdaznSVp8EoX3w==} - vue-component-type-helpers@3.2.4: - resolution: {integrity: sha512-05lR16HeZDcDpB23ku5b5f1fBOoHqFnMiKRr2CiEvbG5Ux4Yi0McmQBOET0dR0nxDXosxyVqv67q6CzS3AK8rw==} + vue-component-type-helpers@3.2.5: + resolution: {integrity: sha512-tkvNr+bU8+xD/onAThIe7CHFvOJ/BO6XCOrxMzeytJq40nTfpGDJuVjyCM8ccGZKfAbGk2YfuZyDMXM56qheZQ==} vue-demi@0.14.10: resolution: {integrity: sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==} @@ -5854,6 +5860,11 @@ packages: typescript: optional: true + vuedraggable@4.1.0: + resolution: {integrity: sha512-FU5HCWBmsf20GpP3eudURW3WdWTKIbEIQxh9/8GE806hydR9qZqRRxRE3RjqX7PkuLuMQG/A7n3cfj9rCEchww==} + peerDependencies: + vue: ^3.0.1 + vueuc@0.4.63: resolution: {integrity: sha512-QJT0z9yYWXdKpUq6f6IrAgJ83e34iTYMCVHjcAP8lCjldG0JzHnDfJYMpPWkNuLB5SdBZCbYGmYTKnTR+ff7CQ==} peerDependencies: @@ -8314,7 +8325,7 @@ snapshots: ts-dedent: 2.2.0 type-fest: 2.19.0 vue: 3.5.8(typescript@4.9.5) - vue-component-type-helpers: 3.2.4 + vue-component-type-helpers: 3.2.5 transitivePeerDependencies: - encoding - supports-color @@ -12133,6 +12144,8 @@ snapshots: slash@3.0.0: {} + sortablejs@1.14.0: {} + source-map-js@1.2.1: {} source-map-support@0.5.21: @@ -12814,7 +12827,7 @@ snapshots: vue-component-type-helpers@2.1.6: {} - vue-component-type-helpers@3.2.4: {} + vue-component-type-helpers@3.2.5: {} vue-demi@0.14.10(vue@3.5.8(typescript@4.9.5)): dependencies: @@ -12891,6 +12904,11 @@ snapshots: optionalDependencies: typescript: 4.9.5 + vuedraggable@4.1.0(vue@3.5.8(typescript@4.9.5)): + dependencies: + sortablejs: 1.14.0 + vue: 3.5.8(typescript@4.9.5) + vueuc@0.4.63(vue@3.5.8(typescript@4.9.5)): dependencies: '@css-render/vue3-ssr': 0.15.14(vue@3.5.8(typescript@4.9.5)) diff --git a/webui/src/api/bangumi.ts b/webui/src/api/bangumi.ts index 24fc2dd7..74ac80ce 100644 --- a/webui/src/api/bangumi.ts +++ b/webui/src/api/bangumi.ts @@ -219,6 +219,19 @@ export const apiBangumi = { return data; }, + /** + * 手动设置番剧的放送星期 + * @param bangumiId - bangumi 的 id + * @param weekday - 0-6 for Mon-Sun, null to reset + */ + async setWeekday(bangumiId: number, weekday: number | null) { + const { data } = await axios.patch( + `api/v1/bangumi/${bangumiId}/weekday`, + { weekday } + ); + return data; + }, + /** * 获取所有需要检查偏移量的 bangumi */ diff --git a/webui/src/i18n/en.json b/webui/src/i18n/en.json index 378af74c..dbaa4ef1 100644 --- a/webui/src/i18n/en.json +++ b/webui/src/i18n/en.json @@ -79,6 +79,14 @@ "type": "Proxy Type", "username": "Username" }, + "security_set": { + "title": "Security", + "hint": "Login whitelist: empty = allow all IPs. MCP whitelist: empty = deny all access.", + "login_whitelist": "Login IP Whitelist", + "login_tokens": "Login API Tokens", + "mcp_whitelist": "MCP IP Whitelist", + "mcp_tokens": "MCP API Tokens" + }, "search_provider_set": { "title": "Search Provider", "add_new": "Add Provider", @@ -287,7 +295,11 @@ "empty_state": { "title": "No Schedule Yet", "subtitle": "Click refresh to fetch this season's broadcast data" - } + }, + "drag_hint": "Drag to assign weekday", + "pinned": "Manually assigned", + "unpin": "Reset to unknown", + "drop_here": "Drop here" }, "setup": { "welcome": { diff --git a/webui/src/i18n/zh-CN.json b/webui/src/i18n/zh-CN.json index a5688541..5081dc19 100644 --- a/webui/src/i18n/zh-CN.json +++ b/webui/src/i18n/zh-CN.json @@ -79,6 +79,14 @@ "type": "类型", "username": "用户名" }, + "security_set": { + "title": "安全设置", + "hint": "登录白名单:为空则允许所有 IP。MCP 白名单:为空则拒绝所有访问。", + "login_whitelist": "登录 IP 白名单", + "login_tokens": "登录 API 令牌", + "mcp_whitelist": "MCP IP 白名单", + "mcp_tokens": "MCP API 令牌" + }, "search_provider_set": { "title": "搜索源设置", "add_new": "添加搜索源", @@ -287,7 +295,11 @@ "empty_state": { "title": "暂无放送表", "subtitle": "点击刷新按钮获取本季度放送数据" - } + }, + "drag_hint": "拖拽以设置放送日", + "pinned": "手动设置", + "unpin": "重置为未知", + "drop_here": "拖放到此处" }, "setup": { "welcome": { diff --git a/webui/src/pages/index/calendar.vue b/webui/src/pages/index/calendar.vue index 8de4d719..d09eea7e 100644 --- a/webui/src/pages/index/calendar.vue +++ b/webui/src/pages/index/calendar.vue @@ -1,5 +1,6 @@