nurse-expense-app | 訪問看護ステーション向け経費精算PWA
2026-03-25 更新訪問看護ステーションのスタッフが、スマホで領収書を撮影しOCRで自動読み取り、経費申請から管理者承認・CSV出力までを完結させるPWAアプリ。
| 領域 | 技術 | 備考 |
|---|---|---|
| Frontend | Next.js 16 (App Router) + TypeScript | React 19 |
| CSS | Tailwind CSS v4 | — |
| 認証 | Firebase Auth | メール/パスワード |
| DB | Firestore | クライアントSDK直接 |
| ストレージ | Firebase Storage | 領収書画像 |
| OCR | GAS + Gemini 2.0 Flash API | 無料枠: 15RPM / 1500RPD |
| PWA | next-pwa | オフライン対応 |
| バリデーション | Zod | — |
| Deploy | Vercel | CDN配信 |
C:\Users\kawag\work\nurse-expense-app\
├── src/
│ ├── app/
│ │ ├── layout.tsx # ルートレイアウト(フォント・PWA・AuthProvider)
│ │ ├── page.tsx # ルートリダイレクト
│ │ ├── login/page.tsx # ログイン・新規登録
│ │ ├── api/export/route.ts # CSV生成 API Route
│ │ └── (dashboard)/
│ │ ├── layout.tsx # 認証ガード付きレイアウト
│ │ ├── submit/page.tsx # 経費申請(撮影→OCR→フォーム)
│ │ ├── history/page.tsx # 申請履歴
│ │ └── admin/page.tsx # 管理者承認 + CSV出力
│ ├── components/
│ │ ├── receipt-camera.tsx # カメラ撮影・プレビュー
│ │ ├── receipt-form.tsx # OCR結果入力フォーム
│ │ ├── status-badge.tsx # ステータスバッジ表示
│ │ ├── nav-header.tsx # ナビゲーション
│ │ └── toast.tsx # トースト通知
│ └── lib/
│ ├── firebase.ts # Firebase初期化(環境変数から設定読込)
│ ├── types.ts # 型定義(Expense, OcrResult, UserProfile等)
│ ├── validators.ts # Zodスキーマ
│ ├── expense-service.ts # Firestore CRUD操作
│ └── auth-context.tsx # Firebase Auth コンテキスト
├── gas/
│ └── ocr.gs # GAS Web App(Gemini OCR本体)
├── public/
│ └── manifest.json # PWAマニフェスト
├── firestore.rules # Firestoreセキュリティルール
├── storage.rules # Storageセキュリティルール
├── next.config.ts
├── CLAUDE.md
└── package.json
| フィールド | 型 | 説明 |
|---|---|---|
| id | string | Firestoreドキュメント ID |
| userId | string | 申請者UID |
| userName | string | 申請者名 |
| receiptUrl | string | Storage上の領収書画像URL |
| amount | number | 金額(税込、1〜1,000,000円) |
| date | string | 日付(YYYY-MM-DD) |
| category | ExpenseCategory | 交通費 / 消耗品 / 通信費 / その他 |
| description | string | 摘要 |
| status | ExpenseStatus | pending / approved / rejected / paid |
| reviewerId | string | null | 承認者UID |
| reviewerComment | string | null | 承認/差し戻しコメント |
| reviewedAt | Date | null | 承認日時 |
| createdAt | Date | 申請日時 |
input[capture="environment"])NEXT_PUBLIC_GAS_OCR_URL)null| コレクション | 操作 | 権限 |
|---|---|---|
| profiles | read | 自分のみ |
| create | 自分のみ(role は必ず staff) | |
| update | 自分のみ(role 変更不可) | |
| delete | 不可 | |
| expenses | read | スタッフ: 自分の申請のみ / 管理者: 全件 |
| create | 認証済みユーザー(status は必ず pending) | |
| update | 管理者のみ(申請内容フィールドの変更不可) | |
| delete | 不可(監査証跡保持) |
receipts/{uid}/)image/*)Firebase Console から手動でプロフィールの role フィールドを "admin" に変更。API経由での昇格はできない設計。
/api/export= + - @ で始まるセルをエスケープ| 変数名 | 説明 |
|---|---|
NEXT_PUBLIC_FIREBASE_API_KEY | Firebase APIキー |
NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN | 認証ドメイン |
NEXT_PUBLIC_FIREBASE_PROJECT_ID | プロジェクトID |
NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET | Storageバケット |
NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID | メッセージング送信者ID |
NEXT_PUBLIC_FIREBASE_APP_ID | アプリID |
NEXT_PUBLIC_GAS_OCR_URL | GAS Web App デプロイURL |
| プロパティ | 説明 |
|---|---|
GEMINI_API_KEY | Google AI Studio で取得したAPIキー |
asia-northeast1(東京)→ 本番モードasia-northeast1有効化後、firebase deploy --only firestore:rules,storage --project nurse-expense-app でSecurity Rulesをデプロイ
gas/ocr.gs の内容をエディタに貼り付けGEMINI_API_KEY を設定NEXT_PUBLIC_GAS_OCR_URL に設定# 開発サーバー起動
npm run dev
# プロダクションビルド
npm run build
# ESLint
npm run lint
public/icon-192.png, public/icon-512.png) 未着手.env.local 設定 完了
nurse-expense-app.env.local にFirebase設定値 + GAS OCR URL設定済みC:\Users\kawag\work\nurse-expense-app\5a1818f Initial commit from Create Next App)