Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 9 additions & 6 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
### 📝 コード生成・ファイル作成時のルール

- **必須**: ファイルの最終行は改行で終わること
- **必須**: 不要な空白行やインデントを挿入しないこと
- **必須**: 不要な空白行やインデント,空白を挿入しないこと
- **必須**: リスト項目の後に不要な空白やプレースホルダーを残さないこと
- **必須**: マークダウンファイルでは適切な改行とスペーシングを保つこと
- **禁止**: テンプレートファイル作成時に `- ` や `1. ` の後に余分なスペースやプレースホルダーを入れること
Expand Down Expand Up @@ -249,13 +249,13 @@ components/ # UI Layer

### UI/UX

- **shadcn/ui**: モダンなUIコンポーネントライブラリ
- **shadcn/ui**: モダンな UI コンポーネントライブラリ
- **lucide-react**: 0.525.0 - アイコンライブラリ
- **react-icons**: 5.3.0 - アイコンライブラリ
- **@radix-ui**: コンポーネントライブラリ
- **@dnd-kit/core**: 6.1.0 - ドラッグアンドドロップ
- **clsx**: 2.1.1 - 条件付きクラス名管理
- **tailwind-merge**: 3.3.1 - TailwindCSSクラスのマージ
- **tailwind-merge**: 3.3.1 - TailwindCSS クラスのマージ
- **class-variance-authority**: 0.7.1 - バリアント管理

### データベース・認証
Expand All @@ -271,19 +271,22 @@ components/ # UI Layer

### shadcn/ui 設定

**shadcn/ui コンポーネントシステム**: プロジェクトはモダンなUIコンポーネントライブラリ shadcn/ui を使用します:
**shadcn/ui コンポーネントシステム**: プロジェクトはモダンな UI コンポーネントライブラリ shadcn/ui を使用します:

**設定ファイル**:
- `components.json`: shadcn/ui 設定(New Yorkスタイル、TypeScript、Tailwind CSS変数使用)

- `components.json`: shadcn/ui 設定(New York スタイル、TypeScript、Tailwind CSS 変数使用)
- `lib/utils.ts`: クラス名結合用ユーティリティ(`cn`関数)

**利用可能なUIコンポーネント**:
**利用可能な UI コンポーネント**:

- `Button`: 複数バリアント対応ボタン
- `Input`: 入力フィールドコンポーネント
- `Checkbox`: チェックボックスコンポーネント
- `ImageUpload`: 画像アップロードコンポーネント
- `PriceInput`: 価格入力コンポーネント

**コンポーネント配置**:

- `components/ui/`: shadcn/ui コンポーネント
- `@/components`, `@/lib/utils` などのパスエイリアス設定済み
23 changes: 2 additions & 21 deletions components/Calendar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,14 @@
import React, {
ReactNode,
useEffect,
useState,
forwardRef,
useImperativeHandle,
} from "react";
import { useDraggable, useDroppable } from "@dnd-kit/core";
import { onAuthStateChanged, User } from "firebase/auth";
import { auth } from "../src/infrastructure/firebase";
import { useAuth } from "../src/contexts/AuthContext";
import { useCalendarMenuPresenter } from "../src/presenters/CalendarPresenter";
import { CalendarMenuService } from "../src/services/CalendarService";
import { FirebaseCalendarMenuRepository } from "../src/repositories/firebase/CalendarRepository";
import { MenuItemList, DisplayMenuItem } from "./MenuItemList";

const calendarMenuRepository = new FirebaseCalendarMenuRepository();
const calendarMenuService = new CalendarMenuService(calendarMenuRepository);

type CalendarProps = {
year?: number;
month?: number;
Expand All @@ -41,7 +34,7 @@ export type CalendarRef = {

const Calendar = forwardRef<CalendarRef, CalendarProps>(
({ year, month, children, monthlyChangeData }, ref) => {
const [user, setUser] = useState<User | null>(null);
const { user } = useAuth();
const currentYear = year || new Date().getFullYear();
const currentMonth = month || new Date().getMonth() + 1;

Expand All @@ -61,20 +54,8 @@ const Calendar = forwardRef<CalendarRef, CalendarProps>(
user,
currentYear,
currentMonth,
calendarMenuService
);

useEffect(() => {
const unsubscribe = onAuthStateChanged(auth, (user) => {
if (user) {
setUser(user);
} else {
setUser(null);
}
});

return () => unsubscribe();
}, [setUser]);

useImperativeHandle(ref, () => ({
refreshData,
Expand Down
19 changes: 3 additions & 16 deletions components/Header.tsx
Original file line number Diff line number Diff line change
@@ -1,29 +1,16 @@
import Image from "next/image";
import { onAuthStateChanged, signInWithPopup, User } from "firebase/auth";
import { useEffect, useState } from "react";
import { signInWithPopup } from "firebase/auth";
import { FaGoogle } from "react-icons/fa";
import { auth, provider } from "../src/infrastructure/firebase";
import { useAuth } from "../src/contexts/AuthContext";
import { Button } from "./ui/button";
import { IoIosArrowBack } from "react-icons/io";

export default function Header() {
const [user, setUser] = useState<User | null>(null);

useEffect(() => {
const unsubscribe = onAuthStateChanged(auth, (user) => {
if (user) {
setUser(user);
} else {
setUser(null);
}
});

return () => unsubscribe();
}, [setUser]);
const { user } = useAuth();

const handleLogout = () => {
auth.signOut();
setUser(null);
};

const signInwithGoogle = async () => {
Expand Down
1 change: 0 additions & 1 deletion components/MenuList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import { FaChevronDown, FaChevronRight } from "react-icons/fa";
import { useDraggable } from "@dnd-kit/core";
import { MenuItem } from "../src/types/Menu";
import { useMenuListPresenter } from "../src/presenters/MenuListPresenter";

const categoryOptions = [
{ value: "1", label: "主菜" },
{ value: "2", label: "副菜" },
Expand Down
35 changes: 5 additions & 30 deletions components/MonthMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,17 @@

import React, {
ReactNode,
useEffect,
useState,
forwardRef,
useImperativeHandle,
} from "react";
import { onAuthStateChanged, User } from "firebase/auth";
import { auth } from "../src/infrastructure/firebase";
import { useAuth } from "../src/contexts/AuthContext";
import { useMonthMenuPresenter } from "../src/presenters/MonthMenuPresenter";
import { MonthMenuService } from "../src/services/MonthMenuService";
import { FirebaseMonthMenuRepository } from "../src/repositories/firebase/MonthMenuRepository";
import { Menu, OriginalMenu } from "../src/types/Menu";
import { useDroppable } from "@dnd-kit/core";
import { MenuItemList, DisplayMenuItem } from "./MenuItemList";
import { ChangeMenuService } from "../src/services/ChangeMenuService";
import { RemainingMenuDialog } from "./RemainingMenuDialog";

const monthMenuRepository = new FirebaseMonthMenuRepository();
const monthMenuService = new MonthMenuService(monthMenuRepository);
const changeMenuService = new ChangeMenuService();

type MonthMenuProps = {
year: number;
month: number;
Expand All @@ -44,7 +35,7 @@ export type MonthMenuRef = {

const MonthMenu = forwardRef<MonthMenuRef, MonthMenuProps>(
({ year, month, onAddMenu, onAddOriginalMenu, onDragEnd, children }, ref) => {
const [user, setUser] = useState<User | null>(null);
const { user } = useAuth();
const [isDialogOpen, setIsDialogOpen] = useState(false);
const [remainingItems, setRemainingItems] = useState<DisplayMenuItem[]>([]);

Expand All @@ -63,19 +54,9 @@ const MonthMenu = forwardRef<MonthMenuRef, MonthMenuProps>(
refreshMonthlyChangeOnly,
refreshAllMenusData,
getMenuNameById,
} = useMonthMenuPresenter(user, year, month, monthMenuService);

useEffect(() => {
const unsubscribe = onAuthStateChanged(auth, (user) => {
if (user) {
setUser(user);
} else {
setUser(null);
}
});
revertChange,
} = useMonthMenuPresenter(user, year, month);

return () => unsubscribe();
}, [setUser]);

useImperativeHandle(ref, () => ({
refreshData,
Expand Down Expand Up @@ -114,13 +95,7 @@ const MonthMenu = forwardRef<MonthMenuRef, MonthMenuProps>(
menuId: string,
isCommonMenu: boolean
) => {
await changeMenuService.removeMonthlyChangeEntry(
year,
month,
menuId,
!isCommonMenu
);
await refreshMonthlyChangeOnly();
await revertChange(menuId, isCommonMenu);
};

if (!user) {
Expand Down
29 changes: 18 additions & 11 deletions components/OriginalMenuEditForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import { Checkbox } from "./ui/checkbox";
import { PriceInput } from "./ui/PriceInput";
import { Button } from "./ui/button";
import { ImageUpload } from "./ui/ImageUpload";
import { ImageService } from "../src/services/ImageService";

type Option = {
value: string;
Expand All @@ -20,13 +19,20 @@ type OriginalMenuEditFormProps = {
onCancel: () => void;
onSave?: (updatedMenu: OriginalMenu, imageFile?: File) => void;
onDelete?: (menuId: string) => void;
// 画像操作用の関数をPropsで受け取る
onUploadImage?: (menuId: string, imageFile: File) => Promise<void>;
onDeleteImage?: (menuId: string) => Promise<void>;
onGetImageUrl?: (menuId: string) => Promise<string | null>;
};

export const OriginalMenuEditForm: FC<OriginalMenuEditFormProps> = ({
menu,
onCancel,
onSave,
onDelete,
onUploadImage,
onDeleteImage,
onGetImageUrl,
}) => {
const [editMenu, setEditMenu] = useState<OriginalMenu>(menu);
// チェックボックスの状態を独立して管理
Expand All @@ -38,27 +44,26 @@ export const OriginalMenuEditForm: FC<OriginalMenuEditFormProps> = ({
const [imageFile, setImageFile] = useState<File | null>(null);
const [existingImageUrl, setExistingImageUrl] = useState<string | null>(null);
const [showExistingImage, setShowExistingImage] = useState(true);
const [imageService] = useState(() => new ImageService());

// 既存画像を取得(新規作成時は除く)
useEffect(() => {
const loadExistingImage = async () => {
if (!menu.id) {
// 新規作成時は画像を取得しない
if (!menu.id || !onGetImageUrl) {
// 新規作成時またはonGetImageUrlが無い場合は画像を取得しない
setExistingImageUrl(null);
return;
}

try {
const url = await imageService.getMenuImageUrlById(menu.id);
const url = await onGetImageUrl(menu.id);
setExistingImageUrl(url);
} catch (error) {
console.error("既存画像の取得に失敗しました:", error);
}
};

loadExistingImage();
}, [menu.id, imageService]);
}, [menu.id, onGetImageUrl]);

const categoryOptions: Option[] = [
{ value: "1", label: "主菜" },
Expand Down Expand Up @@ -173,13 +178,13 @@ export const OriginalMenuEditForm: FC<OriginalMenuEditFormProps> = ({
} else {
// 既存メニューの編集の場合
// 既存画像を削除する場合(UIで非表示にされている場合)
if (existingImageUrl && !showExistingImage && !imageFile) {
await imageService.deleteMenuImage(editMenu.id);
if (existingImageUrl && !showExistingImage && !imageFile && onDeleteImage) {
await onDeleteImage(editMenu.id);
}

// 新しい画像がある場合はアップロード
if (imageFile) {
await imageService.uploadMenuImage(editMenu.id, imageFile);
if (imageFile && onUploadImage) {
await onUploadImage(editMenu.id, imageFile);
}

onSave(editMenu);
Expand All @@ -196,7 +201,9 @@ export const OriginalMenuEditForm: FC<OriginalMenuEditFormProps> = ({
if (onDelete && window.confirm("このメニューを削除しますか?")) {
try {
// 画像も削除
await imageService.deleteMenuImage(editMenu.id);
if (onDeleteImage) {
await onDeleteImage(editMenu.id);
}
onDelete(editMenu.id);
} catch (error) {
console.error("画像の削除に失敗しました:", error);
Expand Down
Loading