Skip to content
Merged
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
72 changes: 72 additions & 0 deletions .coderabbit.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# =========================
# CodeRabbit Configuration (Frontend Repository)
# =========================
language: ko

reviews:
auto_review:
enabled: true
Comment on lines +7 to +8
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

커밋 메시지와 실제 설정의 불일치

커밋 메시지에는 "Fix: removed auto_review"라고 명시되어 있지만, 실제 설정 파일에서는 auto_review.enabledtrue로 설정되어 있습니다.

배포 전에 의도를 명확히 할 필요가 있습니다:

  • auto_review를 활성화하려는 것이 맞다면 커밋 메시지가 잘못되었습니다
  • auto_review를 비활성화하려는 것이라면 설정을 enabled: false로 변경해야 합니다
🤖 Prompt for AI Agents
.coderabbit.yaml lines 7-8: the repository commit claims "Fix: removed
auto_review" but the config still sets auto_review.enabled: true; make the
intent consistent by either updating the commit / PR description to state that
auto_review is enabled, or change the configuration to auto_review.enabled:
false if the goal is to remove/disable auto_review; apply the chosen change and
ensure the commit message and file content match before merging.


# 모든 base/target 브랜치에서 자동 리뷰 수행 (정규식)
base_branches:
- ".*"

instructions: |
이 저장소는 프론트엔드 저장소지만, 백엔드/안드로이드 팀원이 함께 작업할 수 있다.
프론트엔드 숙련도가 낮을 수 있음을 전제로 하여,
문제가 되는 부분이 있다면 단정적으로 지적하지 말고
왜 문제가 될 수 있는지와 맥락을 함께 설명한다.

모든 리뷰 코멘트, 제안, 요약은 반드시 한국어로 작성한다.

리뷰 범위 및 수준(큰그림 중심):
- 변수명, 함수명, 포맷팅, 사소한 코드 스타일은 리뷰하지 않는다.
- 다음 항목을 우선적으로 검토한다:
1) 기존에 이미 존재하는 컴포넌트/훅/유틸이 있는데 새로 만들어 중복이 발생했는지
2) 컴포넌트 책임 분리가 부적절하거나 설계가 어색한지
(과도한 props drilling, 상태 위치 부적절, 비대해진 컴포넌트 등)
3) 화면 흐름, 상태 관리, 데이터 패칭, 에러/로딩 처리의 구조가 일관적인지
4) 성능, 접근성, 모바일 대응에 구조적으로 문제가 될 소지가 있는지
5) 보안, 권한, 민감 정보 노출 가능성이 있는지

장점/단점 및 트레이드오프 분석:
- 변경된 설계나 구현에 대해 반드시 장점과 단점을 모두 설명한다.
- 해당 선택이 어떤 트레이드오프를 가지는지 명확히 서술한다.
- 아래 사항을 질문 형태로 포함할 수 있다:
- 이 선택이 의도된 트레이드오프인지?
- 다른 대안과 비교해 현재 방식을 선택한 이유가 있는지?
- 특별한 이유 없이 선택된 구현이라면 개선 여지가 있는지?

이슈 및 PR 의도 반영:
- PR 설명, 이슈, 커밋 메시지에 드러난 의도를 우선적으로 반영한다.
- 명시된 의도가 없을 경우, 코드만 보고 추론한 의도를 명확히 밝힌 후 리뷰한다.
- 구현이 의도를 충분히 충족하는지 여부를 기준으로 평가한다.

DEV -> PROD PR(릴리즈 PR) 리뷰 규칙:
- 배포 리스크, 장애 가능성, 사용자 영향이 큰 문제 위주로만 리뷰한다.
- 사소한 구조 개선이나 리팩터링 제안은 생략한다.
- 심각한 문제가 없다면 승인 또는 칭찬 위주의 짧은 리뷰로 마무리한다.

리뷰 출력 형식(항상 포함):
1) [작업 요약]
- 코드만 보고 이번 PR에서 수행된 작업을 3~7개 불릿으로 정리한다.
- 단순 나열이 아닌, 기능/구조 단위로 요약한다.

2) [의도/목표]
- PR에서 달성하려는 의도를 정리한다.
- 명시된 의도 + 코드 기반 추론을 함께 포함한다.

3) [설계 평가]
- 선택된 설계/구현의 장점과 단점을 모두 정리한다.
- 트레이드오프 관점에서 평가한다.

4) [주요 리뷰]
- 큰그림 기준의 중요한 이슈만 다룬다.
- 각 항목은 다음 구조를 따른다:
- 문제 또는 관찰 사항
- 영향 범위
- 가능한 대안 또는 개선 방향

5) [릴리즈 PR인 경우]
- 심각한 문제 존재 여부
- 문제가 없다면 승인 또는 긍정적 피드백으로 마무리한다.
7 changes: 5 additions & 2 deletions .github/workflows/dev-cicd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ on:
branches: dev
workflow_dispatch:

permissions:
id-token: write

jobs:
build-and-deploy:
runs-on: ubuntu-latest
Expand Down Expand Up @@ -35,8 +38,8 @@ jobs:
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
role-session-name: frontend-dev-council-deploy
role-to-assume: ${{ secrets.AWS_ROLE_TO_ASSUME }}
aws-region: ${{ secrets.AWS_REGION }}

# S3 업로드
Expand Down
7 changes: 5 additions & 2 deletions .github/workflows/prod-cicd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ on:
branches: [prod]
workflow_dispatch:

permissions:
id-token: write
Comment on lines +8 to +9
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Add contents: read permission to avoid checkout failure.

When you specify an explicit permissions block in GitHub Actions, all unspecified permissions default to none. The workflow uses actions/checkout@v4 at line 22, which requires contents: read permission. Without it, the checkout step will fail.

🔎 Proposed fix
 permissions:
   id-token: write
+  contents: read
🤖 Prompt for AI Agents
In .github/workflows/prod-cicd.yml around lines 8 to 9, the explicit permissions
block only grants id-token: write so actions/checkout@v4 (used later) will fail
because unspecified permissions default to none; add contents: read to the
permissions block (e.g., include "contents: read" alongside "id-token: write")
so the checkout action has the required read access.


jobs:
build-and-deploy:
runs-on: ubuntu-latest
Expand All @@ -31,8 +34,8 @@ jobs:
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
role-session-name: frontend-prod-council-deploy
role-to-assume: ${{ secrets.AWS_ROLE_TO_ASSUME }}
Comment on lines +37 to +38
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

cat -n .github/workflows/prod-cicd.yml

Repository: festabook/frontend

Length of output: 1650


Excellent security improvement with OIDC authentication.

The workflow correctly implements OIDC on the GitHub Actions side with the permissions.id-token: write block and aws-actions/configure-aws-credentials using role assumption. This eliminates long-lived credentials and aligns with security best practices.

Before merging, verify the AWS infrastructure is configured:

  • GitHub is registered as an OIDC identity provider in your AWS account
  • The IAM role referenced in AWS_ROLE_TO_ASSUME has a trust policy allowing GitHub Actions from this repository
  • The role has permissions for S3 sync and CloudFront invalidation
🤖 Prompt for AI Agents
In .github/workflows/prod-cicd.yml around lines 37-38, the workflow uses
role-to-assume: ${{ secrets.AWS_ROLE_TO_ASSUME }} with OIDC but the PR needs you
to verify and (if missing) configure the AWS side: register GitHub as an OIDC
identity provider in the AWS account, ensure the IAM role referenced by
AWS_ROLE_TO_ASSUME exists and its trust policy allows GitHub Actions from this
repository (repo-specific subject condition), confirm the secret
AWS_ROLE_TO_ASSUME contains the exact role ARN, and grant that role the
necessary IAM permissions for S3 sync and CloudFront invalidation so the
workflow can assume it successfully.

aws-region: ${{ secrets.AWS_REGION }}

- name: Upload build files to S3
Expand Down
2 changes: 1 addition & 1 deletion src/components/layout/Sidebar.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ const Sidebar = ({ open, setOpen }) => {
className={`text-xl font-bold cursor-pointer transition-all duration-300 whitespace-nowrap overflow-hidden text-ellipsis ${textVisible ? 'opacity-100' : 'opacity-0'}`}
onClick={() => setPage('home')}
>
Festabook
festabook
</h1>
</div>
{/* 닫기 버튼: 이제 가장 오른쪽 */}
Expand Down
4 changes: 2 additions & 2 deletions src/components/modals/AnnouncementModal.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ const AnnouncementModal = ({ notice, onSave, onClose, showToast }) => {
const isSaveDisabled = isTitleOverflow || isContentOverflow;

return (
<Modal isOpen={true} onClose={onClose}>
<Modal isOpen={true} onClose={onClose} enableMobileLayout={true}>
<h3 className="text-xl font-bold mb-6">
{notice ? "공지사항 수정" : "새 공지사항"}
</h3>
Expand Down Expand Up @@ -184,7 +184,7 @@ const AnnouncementDetailModal = ({ notice, onClose }) => {
}, [onClose]);

return (
<Modal isOpen={true} onClose={onClose}>
<Modal isOpen={true} onClose={onClose} enableMobileLayout={true}>
<h3 className="text-xl font-bold mb-6">공지사항 상세</h3>
<div className="space-y-4">
<div>
Expand Down
2 changes: 1 addition & 1 deletion src/components/modals/EventDateModal.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ const EventDateModal = ({ onSave, onClose, defaultDate }) => {
}, [date]);

return (
<Modal isOpen={true} onClose={onClose} maxWidth="max-w-sm">
<Modal isOpen={true} onClose={onClose} maxWidth="max-w-sm" enableMobileLayout={true}>
<h3 className="text-lg font-bold mb-4">새 날짜 추가</h3>
<div className="space-y-2">
<div className="grid grid-cols-3 gap-2 w-full">
Expand Down
2 changes: 1 addition & 1 deletion src/components/modals/EventModal.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ const EventModal = ({ event, onSave, onClose, availableDates, activeDate, showTo
}, [form]); // form이 변경될 때마다 이벤트 리스너 재등록

return (
<Modal isOpen={true} onClose={onClose} maxWidth="max-w-md">
<Modal isOpen={true} onClose={onClose} maxWidth="max-w-md" enableMobileLayout={true}>
<h3 className="text-xl font-bold mb-6">{event ? '이벤트 수정' : '새 이벤트'}</h3>
<div className="space-y-4">
<div>
Expand Down
2 changes: 1 addition & 1 deletion src/components/modals/FaqModal.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ const FaqModal = ({ faq, onSave, onClose, showToast }) => {
}, [question, answer, onClose, handleSave]);

return (
<Modal isOpen={true} onClose={onClose} maxWidth="max-w-lg">
<Modal isOpen={true} onClose={onClose} maxWidth="max-w-lg" enableMobileLayout={true}>
<h3 className="text-xl font-bold mb-6">{faq ? 'FAQ 수정' : '새 FAQ 등록'}</h3>
<div className="space-y-4">
<div>
Expand Down
37 changes: 23 additions & 14 deletions src/components/modals/FestivalImagesModal.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React, { useState, useEffect, useCallback } from 'react';
import Modal from '../common/Modal';
import AddImageModal from './AddImageModal';
import { festivalAPI } from '../../utils/api';
import { useIsMobile } from '../layout/UseIsMobile';

const FestivalImagesModal = ({ isOpen, onClose, festival, showToast, onUpdate }) => {
const [selectedImage, setSelectedImage] = useState(null);
Expand All @@ -11,6 +12,7 @@ const FestivalImagesModal = ({ isOpen, onClose, festival, showToast, onUpdate })
const [draggedItem, setDraggedItem] = useState(null);
const [selectedImageToDelete, setSelectedImageToDelete] = useState(null);
const [showAddImageModal, setShowAddImageModal] = useState(false);
const isMobile = useIsMobile();

const handleCancelMode = useCallback(() => {
setIsReorderMode(false);
Expand Down Expand Up @@ -193,11 +195,16 @@ const FestivalImagesModal = ({ isOpen, onClose, festival, showToast, onUpdate })
};

return (
<Modal isOpen={isOpen} onClose={onClose} maxWidth="max-w-7xl">
<div className="p-6">
<div className="flex justify-between items-center mb-6">
<h2 className="text-2xl font-bold text-gray-900">축제 이미지 관리</h2>
<div className="flex space-x-2">
<Modal
isOpen={isOpen}
onClose={onClose}
maxWidth={isMobile ? 'w-[92vw] max-w-[420px]' : 'max-w-7xl'}
enableMobileLayout={true}
>
<div className={`${isMobile ? 'p-4 max-h-[70vh] overflow-y-auto' : 'p-6 h-[800px]'} flex flex-col`}>
Comment on lines +198 to +204
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

find . -name "FestivalImagesModal.jsx" -type f

Repository: festabook/frontend

Length of output: 109


🏁 Script executed:

cat -n ./src/components/modals/FestivalImagesModal.jsx | sed -n '190,215p'

Repository: festabook/frontend

Length of output: 1456


🏁 Script executed:

find . -name "Modal.jsx" -o -name "Modal.js" -o -name "Modal.tsx" -o -name "Modal.ts" | head -20

Repository: festabook/frontend

Length of output: 95


🏁 Script executed:

cat -n ./src/components/common/Modal.jsx

Repository: festabook/frontend

Length of output: 4018


🏁 Script executed:

find . -name "PlaceImagesModal.jsx" -o -name "LostItemModal.jsx" -o -name "LostItemGuideModal.jsx"

Repository: festabook/frontend

Length of output: 195


🏁 Script executed:

cat -n ./src/components/modals/PlaceImagesModal.jsx | head -100

Repository: festabook/frontend

Length of output: 4561


🏁 Script executed:

cat -n ./src/components/modals/PlaceImagesModal.jsx | grep -A 15 "return ("

Repository: festabook/frontend

Length of output: 2883


🏁 Script executed:

cat -n ./src/components/modals/LostItemModal.jsx | grep -A 15 "return ("

Repository: festabook/frontend

Length of output: 1555


🏁 Script executed:

cat -n ./src/components/modals/LostItemGuideModal.jsx | grep -A 15 "return ("

Repository: festabook/frontend

Length of output: 1051


Remove double padding in FestivalImagesModal inner div.

The Modal component with enableMobileLayout={true} applies padding (px-4 py-4 on mobile, p-6 on desktop). The inner div at line 204 duplicates this padding (p-4 on mobile, p-6 on desktop), resulting in double padding. For consistency with LostItemModal.jsx and LostItemGuideModal.jsx (which apply padding only at the Modal level), remove the padding from the inner div:

 <Modal
     isOpen={isOpen}
     onClose={onClose}
     maxWidth={isMobile ? 'w-[92vw] max-w-[420px]' : 'max-w-7xl'}
     enableMobileLayout={true}
 >
-    <div className={`${isMobile ? 'p-4 max-h-[70vh] overflow-y-auto' : 'p-6 h-[800px]'} flex flex-col`}>
+    <div className={`${isMobile ? 'max-h-[70vh] overflow-y-auto' : 'h-[800px]'} flex flex-col`}>

Additionally, the maxWidth prop at line 201 is redundant on mobile—when enableMobileLayout={true} and isMobile={true}, Modal uses its hardcoded mobile size classes and ignores maxWidth.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<Modal
isOpen={isOpen}
onClose={onClose}
maxWidth={isMobile ? 'w-[92vw] max-w-[420px]' : 'max-w-7xl'}
enableMobileLayout={true}
>
<div className={`${isMobile ? 'p-4 max-h-[70vh] overflow-y-auto' : 'p-6 h-[800px]'} flex flex-col`}>
<Modal
isOpen={isOpen}
onClose={onClose}
maxWidth={isMobile ? 'w-[92vw] max-w-[420px]' : 'max-w-7xl'}
enableMobileLayout={true}
>
<div className={`${isMobile ? 'max-h-[70vh] overflow-y-auto' : 'h-[800px]'} flex flex-col`}>
🤖 Prompt for AI Agents
In src/components/modals/FestivalImagesModal.jsx around lines 198 to 204, the
Modal is receiving a maxWidth prop and the inner div duplicates padding; remove
the maxWidth prop from the Modal (since enableMobileLayout={true} makes mobile
sizing redundant) and strip the padding classes from the inner div so it no
longer includes 'p-4' or 'p-6' (leave only the sizing/overflow and flex classes)
to avoid double padding and match the other modals.

<div className={`flex ${isMobile ? 'flex-col gap-3 items-start' : 'justify-between items-center mb-6'}`}>
<h2 className={`${isMobile ? 'text-xl' : 'text-2xl'} font-bold text-gray-900`}>축제 이미지 관리</h2>
<div className={isMobile ? 'grid grid-cols-2 gap-2 w-full' : 'flex space-x-2'}>
{!isReorderMode && !isDeleteMode && (
<>
<button
Expand Down Expand Up @@ -251,7 +258,7 @@ const FestivalImagesModal = ({ isOpen, onClose, festival, showToast, onUpdate })
)}

{images && images.length > 0 ? (
<div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4 max-h-[600px] overflow-y-auto">
<div className={`grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4 flex-1 overflow-y-auto ${isMobile ? 'mt-4' : ''}`}>
{images.map((image, index) => {
const imageId = image.festivalImageId || image.id || `temp-${index}`;
return (
Expand Down Expand Up @@ -366,14 +373,16 @@ const FestivalImagesModal = ({ isOpen, onClose, festival, showToast, onUpdate })
</div>
)}

<div className="flex justify-end mt-6">
<button
onClick={onClose}
className="bg-gray-300 text-gray-700 px-4 py-2 rounded-lg hover:bg-gray-400 transition-colors"
>
닫기
</button>
</div>
{!isReorderMode && !isDeleteMode && (
<div className={`mt-auto pt-6 ${isMobile ? 'flex flex-col gap-3' : 'flex justify-end space-x-3'}`}>
<button
onClick={onClose}
className={`${isMobile ? 'w-full' : ''} bg-gray-300 text-gray-700 font-bold py-2 px-4 rounded-lg hover:bg-gray-400 transition-all duration-200`}
>
닫기
</button>
</div>
)}
</div>

{/* 새 이미지 추가 모달 (중첩) */}
Expand Down
2 changes: 1 addition & 1 deletion src/components/modals/FestivalInfoModal.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ const FestivalInfoModal = ({ isOpen, onClose, festival, showToast, onUpdate }) =
}, [onClose]);

return (
<Modal isOpen={isOpen} onClose={onClose} maxWidth="max-w-md">
<Modal isOpen={isOpen} onClose={onClose} maxWidth="max-w-md" enableMobileLayout={true}>
<form onSubmit={handleSubmit}>
<h2 className="text-2xl font-bold mb-6 text-center">축제 정보 수정</h2>

Expand Down
2 changes: 1 addition & 1 deletion src/components/modals/LineupAddModal.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@ const LineupAddModal = ({ isOpen, onClose, showToast, onUpdate }) => {
};

return (
<Modal isOpen={isOpen} onClose={handleClose} maxWidth="max-w-md">
<Modal isOpen={isOpen} onClose={handleClose} maxWidth="max-w-md" enableMobileLayout={true}>
<form onSubmit={handleSubmit}>
<h2 className="text-2xl font-bold mb-6 text-center">라인업 추가</h2>

Expand Down
2 changes: 1 addition & 1 deletion src/components/modals/LineupEditModal.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -294,7 +294,7 @@ const LineupEditModal = ({ isOpen, onClose, lineup, showToast, onUpdate }) => {
};

return (
<Modal isOpen={isOpen} onClose={handleClose} maxWidth="max-w-md">
<Modal isOpen={isOpen} onClose={handleClose} maxWidth="max-w-md" enableMobileLayout={true}>
<form onSubmit={handleSubmit}>
<h2 className="text-2xl font-bold mb-6 text-center">라인업 수정</h2>

Expand Down
4 changes: 2 additions & 2 deletions src/components/modals/LostItemGuideModal.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ const LostItemGuideModal = ({ initialGuide, onSave, onClose }) => {
}, [initialGuide]);

return (
<Modal isOpen={true} onClose={onClose} maxWidth="max-w-xl">
<div className="p-6">
<Modal isOpen={true} onClose={onClose} maxWidth="max-w-xl" enableMobileLayout={true}>
<div>
<h2 className="text-2xl font-bold text-gray-900 mb-4">분실물 안내 수정</h2>
<div className="flex items-center justify-between mb-3">
<p className="text-gray-500 text-sm">분실물 수령/제보 가이드를 입력하세요.</p>
Expand Down
4 changes: 2 additions & 2 deletions src/components/modals/LostItemModal.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -147,8 +147,8 @@ const LostItemModal = ({ item, onSave, onClose, showToast }) => {
};

return (
<Modal isOpen={true} onClose={handleClose} maxWidth="max-w-md">
<div className="p-6">
<Modal isOpen={true} onClose={handleClose} maxWidth="max-w-md" enableMobileLayout={true}>
<div>
<h2 className="text-2xl font-bold text-gray-900 mb-6 text-center">
{item ? '분실물 정보 수정' : '새 분실물 등록'}
</h2>
Expand Down
2 changes: 1 addition & 1 deletion src/components/modals/PasswordChangeModal.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ const PasswordChangeModal = ({ isOpen, onClose, showToast }) => {
}, [onClose]);

return (
<Modal isOpen={isOpen} onClose={onClose} maxWidth="max-w-md">
<Modal isOpen={isOpen} onClose={onClose} maxWidth="max-w-md" enableMobileLayout={true}>
<form onSubmit={handleSubmit}>
<h2 className="text-2xl font-bold mb-6 text-center">비밀번호 변경</h2>

Expand Down
2 changes: 1 addition & 1 deletion src/components/modals/PlaceImagesModal.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -411,7 +411,7 @@ const PlaceImagesModal = ({ place, onUpdate, onClose }) => {
</div>
</div>

<div className="mb-4 p-3 bg-blue-50 border border-blue-200 rounded-lg">
<div className={`mb-4 p-3 bg-blue-50 border border-blue-200 rounded-lg ${isMobile ? 'mt-4' : ''}`}>
<p className="text-blue-800 text-sm">
📸 플레이스 이미지는 최대 5개까지 저장할 수 있습니다.
</p>
Expand Down
2 changes: 1 addition & 1 deletion src/components/modals/PushNotificationConfirmModal.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ const PushNotificationConfirmModal = ({ notice, onConfirm, onCancel }) => {
}, [onConfirm, onCancel]);

return (
<Modal isOpen={true} onClose={onCancel} maxWidth="max-w-2xl">
<Modal isOpen={true} onClose={onCancel} maxWidth="max-w-2xl" enableMobileLayout={true}>
<div className="space-y-6">
<div>
<h3 className="text-xl font-bold text-gray-800 mb-2">푸시 알림 전송 확인</h3>
Expand Down
6 changes: 4 additions & 2 deletions src/pages/CouncilLoginPage.jsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import React, { useState } from 'react';
import { councilLogin } from '../auth/councilAuth';
import { useIsMobile } from '../components/layout/UseIsMobile';

const CouncilLoginPage = ({ onSuccess }) => {
const [username, setUsername] = useState('');
const [password, setPassword] = useState('');
const [loading, setLoading] = useState(false);
const [error, setError] = useState('');
const isMobile = useIsMobile();

const handleSubmit = async (e) => {
e.preventDefault();
Expand Down Expand Up @@ -37,13 +39,13 @@ const CouncilLoginPage = ({ onSuccess }) => {
</div>

{/* Background logo and tagline */}
<div className="absolute inset-0 flex flex-col items-center justify-start pt-20 pointer-events-none">
<div className={`absolute inset-0 flex flex-col items-center justify-start pointer-events-none ${isMobile ? 'pt-12' : 'pt-20'}`}>
<img src="/festabook-logo.png" alt="FestaBook" className="h-96 w-auto object-contain mb-4" />

</div>

{/* Foreground container */}
<div className="relative w-full max-w-md bg-white p-8 rounded-xl shadow-lg">
<div className={`relative bg-white rounded-xl shadow-lg ${isMobile ? 'w-[92vw] max-w-[420px] mx-4 px-4 py-4' : 'w-full max-w-md p-8'}`}>
<div className="flex flex-col items-center mb-6">
<h1 className="text-2xl font-semibold text-gray-800 mb-2">로그인</h1>
<p className="text-gray-400 text-sm opacity-50">흩어진 정보를 하나로, 축제를 한 권에 담다</p>
Expand Down
6 changes: 4 additions & 2 deletions src/pages/FestivalPage.jsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import React, { useState, useEffect, useRef } from 'react';
import { useModal } from '../hooks/useModal';
import { festivalAPI, lineupAPI } from '../utils/api';
import { useIsMobile } from '../components/layout/UseIsMobile';

const FestivalPage = () => {
const { openModal, showToast } = useModal();
const isMobile = useIsMobile();
const [festival, setFestival] = useState(null);
const [lineups, setLineups] = useState([]);
const [loading, setLoading] = useState(true);
Expand Down Expand Up @@ -137,15 +139,15 @@ const FestivalPage = () => {
<div className="w-full">
{/* 헤더 섹션 */}
<div className="mb-8">
<div className="flex justify-between items-center">
<div className={isMobile ? 'flex flex-col' : 'flex justify-between items-center'}>
<div>
<h1 className="text-3xl font-bold text-gray-900 mb-2">
{festival.universityName} 관리
</h1>
</div>
<button
onClick={() => openModal('passwordChange')}
className="flex items-center space-x-2 px-3 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-md hover:bg-gray-50 focus:outline-none transition-colors duration-200"
className={`flex items-center space-x-2 px-3 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-md hover:bg-gray-50 focus:outline-none transition-colors duration-200 ${isMobile ? 'self-start mt-2' : ''}`}
>
<svg className="w-4 h-4 text-gray-500" fill="currentColor" viewBox="0 0 24 24">
<path d="M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z"/>
Expand Down