ba-cafe

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

commit c88e3d4fd04f2ca72381434bc7e8eeb69ef82403
parent 56f0dfe982ed7b790e64cb69652cb9a45fbf362f
Author: Sunny <122193933+Sunny-JP@users.noreply.github.com>
Date:   Sun,  1 Feb 2026 01:25:48 +0900

Merge pull request #44 from Sunny-JP/v4-sunny-dev

V4 sunny dev
Diffstat:
MREADME.md | 14++++++++++++++
Mnext.config.ts | 14+++++++-------
Mpackage-lock.json | 111++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------------
Mpackage.json | 4+++-
Msrc/app/globals.css | 39++++++++++++++++++++++++++-------------
Msrc/app/layout.tsx | 2++
Msrc/components/BottomNavBar.tsx | 35+++++++++++++++++++++--------------
7 files changed, 144 insertions(+), 75 deletions(-)

diff --git a/README.md b/README.md @@ -1,5 +1,19 @@ # Cafe Timer +「Cafe Timer」は、ゲーム『ブルーアーカイブ』のカフェ機能を支援する非公式ファンメイドツールです。<br /> +生徒さんのカフェ訪問タイミングを通知でお知らせし、先生方の業務をサポートします。 + +### 主な機能 +- 生徒さんの再タップ可能タイミングのカウントダウン +- 生徒さんの訪問タイミング(4:00/16:00 JST)のカウントダウン +- 招待券のクールタイム管理 +- タップ履歴のカレンダー表示 + +### 対応プラットフォーム +PCおよびスマートフォンの最新ブラウザで動作します。<br /> +PWAに対応しているため、ホーム画面に追加してアプリのように使うことができます。 +<br /><br /> + ## 技術スタック * **Framework:** Next.js (App Router) diff --git a/next.config.ts b/next.config.ts @@ -2,17 +2,17 @@ import type { NextConfig } from "next"; import withPWAInit from "@ducanh2912/next-pwa"; const cspHeader = ` - default-src 'self'; - script-src 'self' 'unsafe-eval' 'unsafe-inline' https://*.onesignal.com https://onesignal.com https://static.cloudflareinsights.com; +default-src 'self'; + script-src 'self' 'unsafe-eval' 'unsafe-inline' https://*.onesignal.com https://onesignal.com https://static.cloudflareinsights.com https://www.googletagmanager.com; style-src 'self' 'unsafe-inline'; - img-src 'self' blob: data: https://lh3.googleusercontent.com https://cdn.discordapp.com https://*.onesignal.com; + img-src 'self' blob: data: https://lh3.googleusercontent.com https://cdn.discordapp.com https://*.onesignal.com https://www.google-analytics.com https://www.googletagmanager.com; font-src 'self'; - connect-src 'self' https://*.supabase.co wss://*.supabase.co https://*.onesignal.com https://onesignal.com https://cloudflareinsights.com; + connect-src 'self' https://*.supabase.co wss://*.supabase.co https://*.onesignal.com https://onesignal.com https://cloudflareinsights.com https://www.google-analytics.com; object-src 'none'; base-uri 'self'; - form-action 'self'; - frame-ancestors 'none'; upgrade-insecure-requests; + frame-ancestors 'none'; + form-action 'self'; `.replace(/\s{2,}/g, " ").trim(); const nextConfig: NextConfig = { @@ -48,7 +48,7 @@ const nextConfig: NextConfig = { ], }, { - source: '/(.*)', + source: '/:path*', headers: [ { key: 'Content-Security-Policy', value: cspHeader }, { key: 'X-Frame-Options', value: 'DENY' }, diff --git a/package-lock.json b/package-lock.json @@ -9,13 +9,15 @@ "version": "0.1.0", "dependencies": { "@ducanh2912/next-pwa": "^10.2.9", + "@next/third-parties": "^16.1.6", "@react-oauth/google": "^0.12.2", "@supabase/supabase-js": "^2.89.0", "chart.js": "^4.5.1", "date-fns": "^4.1.0", "date-fns-tz": "^3.2.0", "html-to-image": "^1.11.13", - "next": "16.0.10", + "lucide-react": "^0.563.0", + "next": "^16.1.6", "react": "19.2.3", "react-chartjs-2": "^5.3.1", "react-dom": "19.2.3", @@ -2309,9 +2311,9 @@ } }, "node_modules/@next/env": { - "version": "16.0.10", - "resolved": "https://registry.npmjs.org/@next/env/-/env-16.0.10.tgz", - "integrity": "sha512-8tuaQkyDVgeONQ1MeT9Mkk8pQmZapMKFh5B+OrFUlG3rVmYTXcXlBetBgTurKXGaIZvkoqRT9JL5K3phXcgang==", + "version": "16.1.6", + "resolved": "https://registry.npmjs.org/@next/env/-/env-16.1.6.tgz", + "integrity": "sha512-N1ySLuZjnAtN3kFnwhAwPvZah8RJxKasD7x1f8shFqhncnWZn4JMfg37diLNuoHsLAlrDfM3g4mawVdtAG8XLQ==", "license": "MIT" }, "node_modules/@next/eslint-plugin-next": { @@ -2325,9 +2327,9 @@ } }, "node_modules/@next/swc-darwin-arm64": { - "version": "16.0.10", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.0.10.tgz", - "integrity": "sha512-4XgdKtdVsaflErz+B5XeG0T5PeXKDdruDf3CRpnhN+8UebNa5N2H58+3GDgpn/9GBurrQ1uWW768FfscwYkJRg==", + "version": "16.1.6", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.1.6.tgz", + "integrity": "sha512-wTzYulosJr/6nFnqGW7FrG3jfUUlEf8UjGA0/pyypJl42ExdVgC6xJgcXQ+V8QFn6niSG2Pb8+MIG1mZr2vczw==", "cpu": [ "arm64" ], @@ -2341,9 +2343,9 @@ } }, "node_modules/@next/swc-darwin-x64": { - "version": "16.0.10", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.0.10.tgz", - "integrity": "sha512-spbEObMvRKkQ3CkYVOME+ocPDFo5UqHb8EMTS78/0mQ+O1nqE8toHJVioZo4TvebATxgA8XMTHHrScPrn68OGw==", + "version": "16.1.6", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.1.6.tgz", + "integrity": "sha512-BLFPYPDO+MNJsiDWbeVzqvYd4NyuRrEYVB5k2N3JfWncuHAy2IVwMAOlVQDFjj+krkWzhY2apvmekMkfQR0CUQ==", "cpu": [ "x64" ], @@ -2357,9 +2359,9 @@ } }, "node_modules/@next/swc-linux-arm64-gnu": { - "version": "16.0.10", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.0.10.tgz", - "integrity": "sha512-uQtWE3X0iGB8apTIskOMi2w/MKONrPOUCi5yLO+v3O8Mb5c7K4Q5KD1jvTpTF5gJKa3VH/ijKjKUq9O9UhwOYw==", + "version": "16.1.6", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.1.6.tgz", + "integrity": "sha512-OJYkCd5pj/QloBvoEcJ2XiMnlJkRv9idWA/j0ugSuA34gMT6f5b7vOiCQHVRpvStoZUknhl6/UxOXL4OwtdaBw==", "cpu": [ "arm64" ], @@ -2373,9 +2375,9 @@ } }, "node_modules/@next/swc-linux-arm64-musl": { - "version": "16.0.10", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.0.10.tgz", - "integrity": "sha512-llA+hiDTrYvyWI21Z0L1GiXwjQaanPVQQwru5peOgtooeJ8qx3tlqRV2P7uH2pKQaUfHxI/WVarvI5oYgGxaTw==", + "version": "16.1.6", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.1.6.tgz", + "integrity": "sha512-S4J2v+8tT3NIO9u2q+S0G5KdvNDjXfAv06OhfOzNDaBn5rw84DGXWndOEB7d5/x852A20sW1M56vhC/tRVbccQ==", "cpu": [ "arm64" ], @@ -2389,9 +2391,9 @@ } }, "node_modules/@next/swc-linux-x64-gnu": { - "version": "16.0.10", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.0.10.tgz", - "integrity": "sha512-AK2q5H0+a9nsXbeZ3FZdMtbtu9jxW4R/NgzZ6+lrTm3d6Zb7jYrWcgjcpM1k8uuqlSy4xIyPR2YiuUr+wXsavA==", + "version": "16.1.6", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.1.6.tgz", + "integrity": "sha512-2eEBDkFlMMNQnkTyPBhQOAyn2qMxyG2eE7GPH2WIDGEpEILcBPI/jdSv4t6xupSP+ot/jkfrCShLAa7+ZUPcJQ==", "cpu": [ "x64" ], @@ -2405,9 +2407,9 @@ } }, "node_modules/@next/swc-linux-x64-musl": { - "version": "16.0.10", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.0.10.tgz", - "integrity": "sha512-1TDG9PDKivNw5550S111gsO4RGennLVl9cipPhtkXIFVwo31YZ73nEbLjNC8qG3SgTz/QZyYyaFYMeY4BKZR/g==", + "version": "16.1.6", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.1.6.tgz", + "integrity": "sha512-oicJwRlyOoZXVlxmIMaTq7f8pN9QNbdes0q2FXfRsPhfCi8n8JmOZJm5oo1pwDaFbnnD421rVU409M3evFbIqg==", "cpu": [ "x64" ], @@ -2421,9 +2423,9 @@ } }, "node_modules/@next/swc-win32-arm64-msvc": { - "version": "16.0.10", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.0.10.tgz", - "integrity": "sha512-aEZIS4Hh32xdJQbHz121pyuVZniSNoqDVx1yIr2hy+ZwJGipeqnMZBJHyMxv2tiuAXGx6/xpTcQJ6btIiBjgmg==", + "version": "16.1.6", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.1.6.tgz", + "integrity": "sha512-gQmm8izDTPgs+DCWH22kcDmuUp7NyiJgEl18bcr8irXA5N2m2O+JQIr6f3ct42GOs9c0h8QF3L5SzIxcYAAXXw==", "cpu": [ "arm64" ], @@ -2437,9 +2439,9 @@ } }, "node_modules/@next/swc-win32-x64-msvc": { - "version": "16.0.10", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.0.10.tgz", - "integrity": "sha512-E+njfCoFLb01RAFEnGZn6ERoOqhK1Gl3Lfz1Kjnj0Ulfu7oJbuMyvBKNj/bw8XZnenHDASlygTjZICQW+rYW1Q==", + "version": "16.1.6", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.1.6.tgz", + "integrity": "sha512-NRfO39AIrzBnixKbjuo2YiYhB6o9d8v/ymU9m/Xk8cyVk+k7XylniXkHwjs4s70wedVffc6bQNbufk5v0xEm0A==", "cpu": [ "x64" ], @@ -2452,6 +2454,19 @@ "node": ">= 10" } }, + "node_modules/@next/third-parties": { + "version": "16.1.6", + "resolved": "https://registry.npmjs.org/@next/third-parties/-/third-parties-16.1.6.tgz", + "integrity": "sha512-/cLY1egaH529ylSMSK+C8dA3nWDLL4hOFR4fca9OLWWxjcNwzsbuq2pPb/tmdWL9Zj3K1nTjd1pWQoSlaDQ0VA==", + "license": "MIT", + "dependencies": { + "third-party-capital": "1.0.20" + }, + "peerDependencies": { + "next": "^13.0.0 || ^14.0.0 || ^15.0.0 || ^16.0.0-beta.0", + "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -7128,6 +7143,15 @@ "yallist": "^3.0.2" } }, + "node_modules/lucide-react": { + "version": "0.563.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.563.0.tgz", + "integrity": "sha512-8dXPB2GI4dI8jV4MgUDGBeLdGk8ekfqVZ0BdLcrRzocGgG75ltNEmWS+gE7uokKF/0oSUuczNDT+g9hFJ23FkA==", + "license": "ISC", + "peerDependencies": { + "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, "node_modules/magic-string": { "version": "0.30.21", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", @@ -7276,13 +7300,14 @@ "peer": true }, "node_modules/next": { - "version": "16.0.10", - "resolved": "https://registry.npmjs.org/next/-/next-16.0.10.tgz", - "integrity": "sha512-RtWh5PUgI+vxlV3HdR+IfWA1UUHu0+Ram/JBO4vWB54cVPentCD0e+lxyAYEsDTqGGMg7qpjhKh6dc6aW7W/sA==", + "version": "16.1.6", + "resolved": "https://registry.npmjs.org/next/-/next-16.1.6.tgz", + "integrity": "sha512-hkyRkcu5x/41KoqnROkfTm2pZVbKxvbZRuNvKXLRXxs3VfyO0WhY50TQS40EuKO9SW3rBj/sF3WbVwDACeMZyw==", "license": "MIT", "dependencies": { - "@next/env": "16.0.10", + "@next/env": "16.1.6", "@swc/helpers": "0.5.15", + "baseline-browser-mapping": "^2.8.3", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", "styled-jsx": "5.1.6" @@ -7294,14 +7319,14 @@ "node": ">=20.9.0" }, "optionalDependencies": { - "@next/swc-darwin-arm64": "16.0.10", - "@next/swc-darwin-x64": "16.0.10", - "@next/swc-linux-arm64-gnu": "16.0.10", - "@next/swc-linux-arm64-musl": "16.0.10", - "@next/swc-linux-x64-gnu": "16.0.10", - "@next/swc-linux-x64-musl": "16.0.10", - "@next/swc-win32-arm64-msvc": "16.0.10", - "@next/swc-win32-x64-msvc": "16.0.10", + "@next/swc-darwin-arm64": "16.1.6", + "@next/swc-darwin-x64": "16.1.6", + "@next/swc-linux-arm64-gnu": "16.1.6", + "@next/swc-linux-arm64-musl": "16.1.6", + "@next/swc-linux-x64-gnu": "16.1.6", + "@next/swc-linux-x64-musl": "16.1.6", + "@next/swc-win32-arm64-msvc": "16.1.6", + "@next/swc-win32-x64-msvc": "16.1.6", "sharp": "^0.34.4" }, "peerDependencies": { @@ -8695,6 +8720,12 @@ } } }, + "node_modules/third-party-capital": { + "version": "1.0.20", + "resolved": "https://registry.npmjs.org/third-party-capital/-/third-party-capital-1.0.20.tgz", + "integrity": "sha512-oB7yIimd8SuGptespDAZnNkzIz+NWaJCu2RMsbs4Wmp9zSDUM8Nhi3s2OOcqYuv3mN4hitXc8DVx+LyUmbUDiA==", + "license": "ISC" + }, "node_modules/tinyglobby": { "version": "0.2.15", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", diff --git a/package.json b/package.json @@ -11,13 +11,15 @@ }, "dependencies": { "@ducanh2912/next-pwa": "^10.2.9", + "@next/third-parties": "^16.1.6", "@react-oauth/google": "^0.12.2", "@supabase/supabase-js": "^2.89.0", "chart.js": "^4.5.1", "date-fns": "^4.1.0", "date-fns-tz": "^3.2.0", "html-to-image": "^1.11.13", - "next": "16.0.10", + "lucide-react": "^0.563.0", + "next": "^16.1.6", "react": "19.2.3", "react-chartjs-2": "^5.3.1", "react-dom": "19.2.3", diff --git a/src/app/globals.css b/src/app/globals.css @@ -11,7 +11,7 @@ --secondary-foreground: #5a7d9a; --muted: #d0e1f0; --muted-foreground: #8ba8c2; - --nav-foreground: #607d8b; + --nav-foreground: #394a52; --tap-h: 200; --tap-s: 100%; --tap-bg-0: #ffffff; @@ -32,11 +32,11 @@ --card-foreground: #f5f0f7; --primary: hsl(var(--tap-h) var(--tap-s) 50%); --primary-foreground: #f5f0f7; - --secondary: #d7d5e8; + --secondary: #d1d7e9; --secondary-foreground: #a0abc2; --muted: #4b5068; --muted-foreground: #7d8dad; - --nav-foreground: #96a4bd; + --nav-foreground: #a3a5aa; --tap-h: 235; --tap-s: 40%; --tap-bg-0: #0f0e1a; @@ -89,7 +89,7 @@ body { color: var(--foreground); font-family: var(--font-mandali), var(--font-kosugi), "Helvetica Neue", Arial, "Hiragino Kaku Gothic ProN", "Hiragino Sans", Meiryo, sans-serif; font-weight: 700; - padding-bottom: calc(var(--bottom-nav-height) + env(safe-area-inset-bottom)); + padding-bottom: calc(var(--bottom-nav-height)); } .btn-setting { @@ -110,28 +110,41 @@ body { .btn-nav { flex: 1 1 0%; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; padding-top: 14px; - padding-bottom: 14px; - text-align: center; - font-size: 4.5cqw; - line-height: 1.25; - font-weight: 700; + padding-bottom: 16px; color: var(--nav-foreground); background-color: var(--background); transition: all 0.2s ease-in-out; + cursor: pointer; +} +.btn-nav svg { + transition: stroke-width 0.3s cubic-bezier(0.4, 0, 0.2, 1); + stroke-width: 1.5; +} +.btn-nav.active svg { + stroke-width: 2.2; } .btn-nav:hover { color: var(--primary); + opacity: 50%; } .dark .btn-nav:hover { - color: #f5f0f7; + color: var(--foreground); + opacity: 90%; } .btn-nav.active, .btn-nav:hover.active { - background-color: var(--primary); - color: var(--primary-foreground); + color: var(--primary); + opacity: 100%; +} +.dark .btn-nav.active, +.dark .btn-nav:hover.active { + color: var(--foreground); } - .dashboard-container { container-type: inline-size; display: flex; diff --git a/src/app/layout.tsx b/src/app/layout.tsx @@ -1,5 +1,6 @@ import type { Metadata, Viewport } from "next"; import { Mandali, Kosugi_Maru } from "next/font/google"; +import { GoogleAnalytics } from '@next/third-parties/google'; import "./globals.css"; import Providers from "./providers"; @@ -65,6 +66,7 @@ export default function RootLayout({ return ( <html lang="ja" className="h-full"> <body className={`${mandali.variable} ${kosugi.variable} h-full`}> + <GoogleAnalytics gaId="G-FQSKNGBJ7W" /> <Providers> {children} </Providers> diff --git a/src/components/BottomNavBar.tsx b/src/components/BottomNavBar.tsx @@ -1,4 +1,5 @@ import React from 'react'; +import { Timer, CalendarDays } from 'lucide-react'; type Tab = 'timer' | 'history'; @@ -8,27 +9,32 @@ interface BottomNavBarProps { } const BottomNavBar: React.FC<BottomNavBarProps> = ({ activeTab, setActiveTab }) => { - const tabs: { id: Tab; label: string }[] = [ - { id: 'timer', label: 'Timer' }, - { id: 'history', label: 'History' }, + const tabs = [ + { id: 'timer' as const, icon: Timer }, + { id: 'history' as const, icon: CalendarDays }, ]; return ( <div - className="fixed bottom-0 left-0 right-0 border-t desktop-hidden flex items-center" + className="fixed bottom-0 left-0 right-0 desktop-hidden" style={{ paddingBottom: 'env(safe-area-inset-bottom)' }} > - {tabs.map((tab) => ( - <button - key={tab.id} - onClick={() => setActiveTab(tab.id)} - className={`btn-nav ${activeTab === tab.id ? 'active' : ''}`} - > - {tab.label} - </button> - ))} + {tabs.map((tab) => { + const Icon = tab.icon; + const isActive = activeTab === tab.id; + + return ( + <button + key={tab.id} + onClick={() => setActiveTab(tab.id)} + className={`btn-nav ${isActive ? 'active' : ''}`} + > + <Icon size={30} /> + </button> + ); + })} </div> ); }; -export default BottomNavBar; +export default BottomNavBar;+ \ No newline at end of file