diff --git a/platform/web/app/daily-card/page.tsx b/platform/web/app/daily-card/page.tsx index 2b5e21f..32e1f22 100644 --- a/platform/web/app/daily-card/page.tsx +++ b/platform/web/app/daily-card/page.tsx @@ -8,13 +8,6 @@ import { ActionableBetCard } from '@/components/ActionableBetCard'; type TabKey = 'all' | 'safe' | 'risk' | 'parlay' | 'sgp'; -const TAB_MAP: Record = { - all: 'ALL', - safe: 'SAFE_SINGLE', - risk: 'HIGH_RISK_SINGLE', - parlay: 'SAFE_PARLAY', - sgp: 'SGP_LOTTERY', -}; const sampleDate = formatToTaipeiTime(new Date().toISOString(), 'yyyy-MM-dd'); const tomorrowSampleDate = formatToTaipeiTime(new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString(), 'yyyy-MM-dd'); diff --git a/platform/web/app/page.tsx b/platform/web/app/page.tsx index 5229883..244c8c6 100644 --- a/platform/web/app/page.tsx +++ b/platform/web/app/page.tsx @@ -259,7 +259,6 @@ export default function Home() { if (!mounted) return; const nextErrors: LoadErrors = {}; - const matchPayload = matchesResult.status === 'fulfilled' ? matchesResult.value : []; const calendarPayload = calendarResult.status === 'fulfilled' ? calendarResult.value.dates : []; const calendarDateList = calendarPayload.map((item) => item.date).sort((a, b) => a.localeCompare(b)); const pinnedDates = [dateWindow.today, dateWindow.tomorrow].filter((date) => calendarDateList.includes(date)); @@ -434,7 +433,6 @@ export default function Home() { const watchOnlyRecommendationCount = recommendationItems.length - liveRecommendationCount; const gate = buildGateState(sourceHealth, errors, liveRecommendationCount, watchOnlyRecommendationCount); - const latestOddsLabel = sourceHealth?.latest_odds_recorded_at ? formatToTaipeiTime(sourceHealth.latest_odds_recorded_at) : '尚無盤口時間'; const latestResultLabel = sourceHealth?.latest_result_synced_at ? formatToTaipeiTime(sourceHealth.latest_result_synced_at) : '尚無賽果時間'; const latestNewsLabel = sourceHealth?.news_status?.run_at ? formatToTaipeiTime(sourceHealth.news_status.run_at) : '尚無新聞排程'; const latestFixturesLabel = sourceHealth?.fixtures_status?.run_at ? formatToTaipeiTime(sourceHealth.fixtures_status.run_at) : '尚無賽程排程'; diff --git a/platform/web/app/portfolio/page.tsx b/platform/web/app/portfolio/page.tsx index 135500a..a42a4f5 100644 --- a/platform/web/app/portfolio/page.tsx +++ b/platform/web/app/portfolio/page.tsx @@ -1,6 +1,6 @@ 'use client'; -import { FormEvent, useEffect, useState } from 'react'; +import { FormEvent, useState } from 'react'; import { analyzePortfolioLeaks, type PortfolioLeaksRequestPayload, diff --git a/platform/web/components/PwaBootstrap.tsx b/platform/web/components/PwaBootstrap.tsx index dbd000d..137abd6 100644 --- a/platform/web/components/PwaBootstrap.tsx +++ b/platform/web/components/PwaBootstrap.tsx @@ -56,12 +56,10 @@ export function PwaBootstrap() { navigator.serviceWorker.addEventListener('message', (event) => { const payload = event.data; if (payload?.type === 'push-trigger') { - // eslint-disable-next-line no-console console.log('PWA push message', payload); } }); } catch (error) { - // eslint-disable-next-line no-console console.warn('PWA bootstrap 失敗', error); } }; diff --git a/platform/web/eslint.config.mjs b/platform/web/eslint.config.mjs new file mode 100644 index 0000000..8291f4b --- /dev/null +++ b/platform/web/eslint.config.mjs @@ -0,0 +1,23 @@ +import { dirname } from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { FlatCompat } from '@eslint/eslintrc'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); + +const compat = new FlatCompat({ + baseDirectory: __dirname, +}); + +const eslintConfig = [ + { + ignores: [ + '.next/**', + 'node_modules/**', + 'next-env.d.ts', + ], + }, + ...compat.extends('next/core-web-vitals', 'next/typescript'), +]; + +export default eslintConfig; diff --git a/platform/web/lib/auth.ts b/platform/web/lib/auth.ts index 5b9398c..6a393ba 100644 --- a/platform/web/lib/auth.ts +++ b/platform/web/lib/auth.ts @@ -1,4 +1,5 @@ import NextAuth, { type DefaultSession } from 'next-auth'; +import type { Adapter } from '@auth/core/adapters'; import Credentials from 'next-auth/providers/credentials'; import { PrismaAdapter } from '@auth/prisma-adapter'; import { prisma } from './db'; @@ -18,7 +19,7 @@ declare module 'next-auth' { } export const { handlers, auth, signIn, signOut } = NextAuth({ - adapter: PrismaAdapter(prisma) as any, + adapter: PrismaAdapter(prisma) as Adapter, providers: [ Credentials({ name: 'Email', diff --git a/platform/web/package-lock.json b/platform/web/package-lock.json index ddcf9d0..0c9af4b 100644 --- a/platform/web/package-lock.json +++ b/platform/web/package-lock.json @@ -20,6 +20,7 @@ "zod": "3.24.1" }, "devDependencies": { + "@eslint/eslintrc": "3.3.1", "@types/node": "22.10.2", "@types/react": "19.0.1", "@types/react-dom": "19.0.1", @@ -212,20 +213,20 @@ } }, "node_modules/@eslint/eslintrc": { - "version": "3.3.5", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.5.tgz", - "integrity": "sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", + "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", "dev": true, "license": "MIT", "dependencies": { - "ajv": "^6.14.0", + "ajv": "^6.12.4", "debug": "^4.3.2", "espree": "^10.0.1", "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", - "js-yaml": "^4.1.1", - "minimatch": "^3.1.5", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", "strip-json-comments": "^3.1.1" }, "engines": { @@ -3467,6 +3468,30 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/eslint/node_modules/@eslint/eslintrc": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.5.tgz", + "integrity": "sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.14.0", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.1", + "minimatch": "^3.1.5", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, "node_modules/espree": { "version": "10.4.0", "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", diff --git a/platform/web/package.json b/platform/web/package.json index b31979a..c7b431f 100644 --- a/platform/web/package.json +++ b/platform/web/package.json @@ -6,7 +6,7 @@ "dev": "next dev -p 3000", "build": "next build", "start": "next start -p 3000", - "lint": "next lint" + "lint": "eslint app components hooks lib --max-warnings=0" }, "dependencies": { "@auth/prisma-adapter": "2.7.4", @@ -30,6 +30,7 @@ "postcss": "8.4.47", "prisma": "5.20.0", "tailwindcss": "3.4.14", - "typescript": "5.6.3" + "typescript": "5.6.3", + "@eslint/eslintrc": "3.3.1" } }