Skip to content

Commit 849a2d8

Browse files
authored
Merge pull request #32 from ut42tech/develop
Add architecture documentation and grade update for Tecnova platform
2 parents 836300b + 062378c commit 849a2d8

7 files changed

Lines changed: 330 additions & 11 deletions

File tree

apps/admin/src/app/(authed)/pre-registrations/page.tsx

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -226,18 +226,20 @@ function ActivatedPreRegistrationsTable({ items }: { items: ActivatedPreRegistra
226226
);
227227
}
228228

229-
const DEFAULT_GRADE: Grade = '小1';
230-
231229
function CreatePreRegistrationForm({ onCreated }: { onCreated: () => Promise<void> }) {
232230
const [fullName, setFullName] = useState('');
233231
const [nickname, setNickname] = useState('');
234-
const [grade, setGrade] = useState<Grade>(DEFAULT_GRADE);
232+
const [grade, setGrade] = useState<Grade | ''>('');
235233
const [registeredAt, setRegisteredAt] = useState(todayInJst());
236234
const [busy, setBusy] = useState(false);
237235

238236
const submit = async (e: FormEvent) => {
239237
e.preventDefault();
240238
if (busy) return;
239+
if (!grade) {
240+
toastError(new Error('学年を選択してください'), '入力内容を確認してください');
241+
return;
242+
}
241243
setBusy(true);
242244
try {
243245
const body: CreatePreRegistrationRequest = {
@@ -253,7 +255,7 @@ function CreatePreRegistrationForm({ onCreated }: { onCreated: () => Promise<voi
253255
toastSuccess(`${created.preRegistrationId} を追加しました`);
254256
setFullName('');
255257
setNickname('');
256-
setGrade(DEFAULT_GRADE);
258+
setGrade('');
257259
setRegisteredAt(todayInJst());
258260
await onCreated();
259261
} catch (e) {
@@ -298,7 +300,7 @@ function CreatePreRegistrationForm({ onCreated }: { onCreated: () => Promise<voi
298300
<Label>学年</Label>
299301
<Select value={grade} onValueChange={(value) => setGrade(value as Grade)}>
300302
<SelectTrigger className="w-full">
301-
<SelectValue />
303+
<SelectValue placeholder="選択してください" />
302304
</SelectTrigger>
303305
<SelectContent>
304306
{GRADES.map((g) => (

docs/architecture.md

Lines changed: 284 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,284 @@
1+
# Tecnova Platform Architecture
2+
3+
This document captures the current Tecnova Nagasaki operations platform and its
4+
planned expansion scope.
5+
6+
The first diagram is a compact current-state overview. The detailed map below it
7+
keeps the broader current and planned scope in one Mermaid `architecture-beta`
8+
diagram. Mermaid brand logos require renderer-level icon-pack registration, so
9+
the portable diagrams use text labels and the brand logos are shown as badges.
10+
11+
## Current Implementation Overview
12+
13+
<p align="center">
14+
<img src="https://img.shields.io/badge/Next.js-16-000?logo=next.js&logoColor=white" alt="Next.js 16">
15+
<img src="https://img.shields.io/badge/React-19-61DAFB?logo=react&logoColor=black" alt="React 19">
16+
<img src="https://img.shields.io/badge/Hono-4-E36002?logo=hono&logoColor=white" alt="Hono 4">
17+
<img src="https://img.shields.io/badge/Cloudflare_Workers-F38020?logo=cloudflare&logoColor=white" alt="Cloudflare Workers">
18+
<img src="https://img.shields.io/badge/Cloudflare_D1-SQLite-F38020?logo=cloudflare&logoColor=white" alt="Cloudflare D1">
19+
<img src="https://img.shields.io/badge/Drizzle_ORM-0.45-C5F74F?logo=drizzle&logoColor=black" alt="Drizzle ORM">
20+
<img src="https://img.shields.io/badge/Google_Sheets-API-34A853?logo=googlesheets&logoColor=white" alt="Google Sheets API">
21+
<img src="https://img.shields.io/badge/Google_OAuth-Better_Auth-4285F4?logo=google&logoColor=white" alt="Google OAuth and Better Auth">
22+
<img src="https://img.shields.io/badge/Vercel-Deploy-000?logo=vercel&logoColor=white" alt="Vercel">
23+
<img src="https://img.shields.io/badge/GitHub_Actions-CI-2088FF?logo=githubactions&logoColor=white" alt="GitHub Actions">
24+
</p>
25+
26+
```mermaid
27+
flowchart LR
28+
subgraph devices["Operation Devices"]
29+
ipad["Reception iPad"]
30+
pc["Admin PC"]
31+
end
32+
33+
subgraph frontend["Vercel Frontend"]
34+
checkin["Checkin PWA<br/>Next.js 16 / React 19<br/>ZXing QR Scan"]
35+
admin["Admin Dashboard<br/>Next.js 16 / React 19"]
36+
ui["Shared UI<br/>shadcn/ui / Tailwind CSS<br/>apiFetch / MeProvider"]
37+
end
38+
39+
subgraph api["Cloudflare Workers API"]
40+
hono["Hono REST API"]
41+
betterAuth["Better Auth<br/>Google OAuth Sessions"]
42+
d1[("Cloudflare D1<br/>SQLite")]
43+
end
44+
45+
subgraph data["Current Data Model"]
46+
participants[("participants")]
47+
events[("events")]
48+
sessions[("sessions")]
49+
mentors[("mentors")]
50+
authTables[("Better Auth tables")]
51+
end
52+
53+
subgraph google["Google Workspace"]
54+
oauth["Google OAuth"]
55+
sheets["Student Google Sheet<br/>Sheets API"]
56+
gas["GAS Webhook"]
57+
drive["Drive Folders"]
58+
end
59+
60+
subgraph repo["Monorepo And Delivery"]
61+
workspace["pnpm / Turborepo<br/>TypeScript / Zod / Drizzle"]
62+
ci["GitHub Actions<br/>Biome / Type Check"]
63+
deploy["Wrangler + Vercel Deploy"]
64+
end
65+
66+
ipad --> checkin
67+
pc --> admin
68+
checkin --> ui
69+
admin --> ui
70+
checkin --> hono
71+
admin --> hono
72+
73+
hono --> betterAuth
74+
betterAuth --> oauth
75+
betterAuth --> mentors
76+
hono --> d1
77+
d1 --> participants
78+
d1 --> events
79+
d1 --> sessions
80+
d1 --> mentors
81+
d1 --> authTables
82+
83+
hono --> sheets
84+
hono -. waitUntil .-> gas
85+
gas --> drive
86+
87+
workspace --> checkin
88+
workspace --> admin
89+
workspace --> hono
90+
workspace --> d1
91+
ci --> deploy
92+
deploy --> checkin
93+
deploy --> admin
94+
deploy --> hono
95+
deploy --> d1
96+
97+
classDef device fill:#f8fafc,stroke:#64748b,color:#0f172a;
98+
classDef frontend fill:#e8f3ff,stroke:#2563eb,color:#0f172a;
99+
classDef backend fill:#fff7ed,stroke:#f97316,color:#0f172a;
100+
classDef db fill:#ecfdf5,stroke:#16a34a,color:#0f172a;
101+
classDef external fill:#fefce8,stroke:#ca8a04,color:#0f172a;
102+
classDef ops fill:#f5f3ff,stroke:#7c3aed,color:#0f172a;
103+
104+
class ipad,pc device;
105+
class checkin,admin,ui frontend;
106+
class hono,betterAuth backend;
107+
class d1,participants,events,sessions,mentors,authTables db;
108+
class oauth,sheets,gas,drive external;
109+
class workspace,ci,deploy ops;
110+
```
111+
112+
## Detailed Architecture Map
113+
114+
```mermaid
115+
architecture-beta
116+
group people(internet)[People And Devices]
117+
service child_kiosk(server)[Child And Reception iPad] in people
118+
service mentor_phone(server)[Mentor Smartphone Future] in people
119+
service admin_pc(server)[Admin PC Browser] in people
120+
service researcher_pc(server)[Researcher Analysis PC Future] in people
121+
service parent_browser(server)[Parent Browser Future] in people
122+
123+
group frontend(cloud)[Vercel Frontend Apps]
124+
service checkin_app(server)[apps checkin Next PWA Current] in frontend
125+
service admin_app(server)[apps admin Next Web Current] in frontend
126+
service mentor_app(server)[apps mentor Next PWA Future] in frontend
127+
service analytics_app(server)[Analytics Dashboard Future] in frontend
128+
service parent_app(server)[Parent View Future] in frontend
129+
130+
group edge(cloud)[Cloudflare Edge Platform]
131+
service worker(server)[apps api Hono Workers Current] in edge
132+
service d1(database)[Cloudflare D1 SQLite Current] in edge
133+
service r2(disk)[Cloudflare R2 Future] in edge
134+
service public_api(server)[Public API Future] in edge
135+
service api_keys(server)[API Key Auth Future] in edge
136+
137+
group auth(cloud)[Authentication Boundary]
138+
service better_auth(server)[Better Auth Current] in auth
139+
service google_oauth(internet)[Google OAuth Current] in auth
140+
service mentor_allowlist(database)[Mentors Allowlist Current] in auth
141+
service session_cookie(disk)[Session Cookies Current] in auth
142+
143+
group google(internet)[Google Workspace Services]
144+
service student_sheet(disk)[Student Sheet Current] in google
145+
service teacher_sheet(disk)[Teacher Sheet Future Sync] in google
146+
service sheets_api(server)[Google Sheets API Current] in google
147+
service gas_drive(server)[GAS Drive Webhook Current] in google
148+
service drive_folder(disk)[Participant Drive Folders Current] in google
149+
service form_feed(server)[Google Form Feed External] in google
150+
151+
group core(server)[Current Backend Domain]
152+
service participants(database)[Participants Current] in core
153+
service events(database)[Events Current] in core
154+
service sessions(database)[Sessions Current] in core
155+
service mentors(database)[Mentors Current] in core
156+
service auth_tables(database)[Better Auth Tables Current] in core
157+
158+
group planned(server)[Planned Domain Model]
159+
service activity_logs(database)[Activity Logs Future] in planned
160+
service activity_categories(database)[Activity Categories Future] in planned
161+
service equipment(database)[Equipment Master Future] in planned
162+
service collaborators(database)[Collaborators Future] in planned
163+
service reflection_ocr(server)[Vision LLM OCR Future] in planned
164+
service csv_export(disk)[CSV Export Future] in planned
165+
service offline_sync(server)[Offline Sync Future] in planned
166+
service crowd_status(server)[Crowd Status Future] in planned
167+
168+
group packages(disk)[Monorepo Shared Packages]
169+
service ui_pkg(disk)[packages ui Current] in packages
170+
service shared_pkg(disk)[packages shared Current] in packages
171+
service db_pkg(disk)[packages db Current] in packages
172+
service schemas_pkg(disk)[Zod Schemas Current] in packages
173+
service sheets_client(disk)[Sheets Client Current] in packages
174+
service migrations(disk)[Drizzle Migrations Current] in packages
175+
176+
group delivery(cloud)[Delivery And Operations]
177+
service github(server)[GitHub Repository Current] in delivery
178+
service actions(server)[GitHub Actions Current] in delivery
179+
service ci(server)[Biome And Type Check Current] in delivery
180+
service deploy_api(server)[Wrangler Deploy Current] in delivery
181+
service vercel_deploy(server)[Vercel Deploy Current] in delivery
182+
service secrets(disk)[Wrangler And Vercel Secrets Current] in delivery
183+
service ops_manual(disk)[Runbook And Rehearsal Current] in delivery
184+
service legacy_import(disk)[Prior Year D1 Import Future] in delivery
185+
186+
child_kiosk:R --> L:checkin_app
187+
mentor_phone:R --> L:mentor_app
188+
admin_pc:R --> L:admin_app
189+
researcher_pc:R --> L:analytics_app
190+
parent_browser:R --> L:parent_app
191+
192+
checkin_app:R --> L:worker
193+
admin_app:R --> L:worker
194+
mentor_app:R --> L:worker
195+
analytics_app:R --> L:worker
196+
parent_app:R --> L:public_api
197+
198+
checkin_app:T --> B:ui_pkg
199+
admin_app:T --> B:ui_pkg
200+
mentor_app:T --> B:ui_pkg
201+
checkin_app:B --> T:shared_pkg
202+
admin_app:B --> T:shared_pkg
203+
mentor_app:B --> T:shared_pkg
204+
205+
worker:L --> R:better_auth
206+
better_auth:R --> L:google_oauth
207+
better_auth:B --> T:session_cookie
208+
better_auth:T --> B:mentor_allowlist
209+
mentor_allowlist:R --> L:mentors
210+
211+
worker:B --> T:d1
212+
d1:B --> T:participants
213+
d1:B --> T:events
214+
d1:B --> T:sessions
215+
d1:B --> T:mentors
216+
d1:B --> T:auth_tables
217+
d1:R --> L:activity_logs
218+
d1:R --> L:activity_categories
219+
d1:R --> L:equipment
220+
d1:R --> L:collaborators
221+
222+
worker:R --> L:sheets_api
223+
sheets_api:R --> L:student_sheet
224+
teacher_sheet:R --> L:student_sheet
225+
form_feed:R --> L:teacher_sheet
226+
worker:R --> L:gas_drive
227+
gas_drive:R --> L:drive_folder
228+
229+
public_api:B --> T:api_keys
230+
public_api:L --> R:worker
231+
public_api:R --> L:crowd_status
232+
worker:R --> L:r2
233+
r2:R --> L:reflection_ocr
234+
reflection_ocr:R --> L:activity_logs
235+
activity_logs:B --> T:csv_export
236+
activity_logs:R --> L:analytics_app
237+
sessions:R --> L:crowd_status
238+
offline_sync:L --> R:checkin_app
239+
offline_sync:R --> L:worker
240+
241+
db_pkg:R --> L:d1
242+
migrations:R --> L:d1
243+
schemas_pkg:R --> L:shared_pkg
244+
shared_pkg:R --> L:worker
245+
sheets_client:R --> L:sheets_api
246+
247+
github:R --> L:actions
248+
actions:R --> L:ci
249+
actions:R --> L:deploy_api
250+
actions:R --> L:vercel_deploy
251+
deploy_api:R --> L:worker
252+
deploy_api:B --> T:migrations
253+
vercel_deploy:R --> L:checkin_app
254+
vercel_deploy:R --> L:admin_app
255+
vercel_deploy:R --> L:mentor_app
256+
secrets:T --> B:worker
257+
secrets:T --> B:checkin_app
258+
secrets:T --> B:admin_app
259+
ops_manual:R --> L:child_kiosk
260+
legacy_import:R --> L:d1
261+
```
262+
263+
## Legend
264+
265+
- `Current` means the component exists in the repository or is already described
266+
as deployed in the handoff notes.
267+
- `Future` means planned Phase 1.5 or Phase 2 scope from the requirements and
268+
MVP documentation.
269+
- `/api/*` and `/checkin/*` both pass through Better Auth and the `mentors`
270+
allowlist. Planned `/public/v1/*` traffic is separated behind API key auth.
271+
- The private teacher-managed sheet remains outside the in-house database. The
272+
platform reads and writes only the student-facing sheet columns required for
273+
operations.
274+
- Cloudflare D1 stores operational data as UTC epoch milliseconds, while event
275+
dates and user-facing displays are resolved in JST.
276+
277+
## Mermaid Notes
278+
279+
- GitHub Markdown renders Mermaid blocks when they are fenced with the
280+
`mermaid` language identifier.
281+
- Mermaid `architecture-beta` starts with `architecture-beta` and supports
282+
`group`, `service`, and directional edge declarations.
283+
- Built-in architecture icons are limited to `cloud`, `database`, `disk`,
284+
`internet`, and `server`; richer vendor icons require registered icon packs.

docs/handoff.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,37 @@ Vercel URL を両方 Worker secret に登録済み。
242242

243243
---
244244

245+
## 学年「その他」追加リリース手順(2026-05-22)
246+
247+
学年の正規値に `その他` を追加し、旧値 `卒業` は運用データ更新で `その他` に置換する。
248+
スプレッドシートの実データは以下の手順で手動更新する。
249+
250+
### デプロイ / 更新順序
251+
252+
1. **新コードをデプロイ**:
253+
- api: `pnpm --filter @tecnova/api deploy`
254+
- admin / checkin: 通常の Vercel デプロイ(main マージで自動)
255+
2. **DB マイグレーション適用(remote)**:
256+
```bash
257+
pnpm --filter @tecnova/api db:apply:remote
258+
```
259+
`0003_graduated_grade_to_other.sql``participants.grade = '卒業'``その他` に更新する。
260+
3. **学生側スプシを手動更新**:
261+
1. 対象スプレッドシートを開き、`participants` シートを確認する
262+
2. 作業前にスプレッドシート全体、または `participants` シートを複製してバックアップする
263+
3. D列「学年」を対象に検索またはフィルタで `卒業` を抽出する
264+
4. 該当セルをすべて `その他` に置換する
265+
5. D列で `卒業` を再検索し、0件であることを確認する
266+
6. `小1``高3``その他` 以外の値が残っていないか、必要に応じてD列を目視確認する
267+
4. **学年上げ運用の確認**:
268+
- スプレッドシート側や外部運用で `卒業` を作っている場合、出力値を `その他` に変更する
269+
5. **動作確認**:
270+
- admin の事前登録追加で学年初期値が未選択になっている
271+
- `その他` を選んで新規追加できる
272+
- 利用者一覧の `その他` フィルタで更新済みデータが表示される
273+
274+
---
275+
245276
## 氏名(fullName)追加リリース手順(2026-05-13 着手)
246277

247278
参加者データに **氏名(本名)** を追加した。学生側スプシは **B列に氏名を挿入** し、既存

docs/mvp.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -234,7 +234,7 @@ async function getOrCreateTodayEvent(
234234
| A | 事前登録ID | text | PRE-2026-0001 形式 | 教員側 |
235235
| B | 氏名 | text | 本名(識別補助) | 教員側 |
236236
| C | ニックネーム | text | メイン識別子 | 教員側 |
237-
| D | 学年 | text | 小1, 小4, 中2 | 教員側 |
237+
| D | 学年 | text | 小1, 小4, 中2, その他 等 | 教員側 |
238238
| E | 事前登録日 | date | YYYY-MM-DD | 教員側 |
239239
| F | 内製ID | text | 26001 等(バックエンドが書き込み) | バックエンド |
240240
| G | アクティベート日時 | datetime | YYYY-MM-DD HH:mm:ss(同上) | バックエンド |

0 commit comments

Comments
 (0)