diff --git a/src/app/dashboard/club/review/page.tsx b/src/app/dashboard/club/review/page.tsx index 3ee87ca..e378205 100644 --- a/src/app/dashboard/club/review/page.tsx +++ b/src/app/dashboard/club/review/page.tsx @@ -235,7 +235,7 @@ export default function ClubReviewPage() { maxLength={1000} onChange={(e) => setReason(e.target.value)} placeholder="예: 모임 장면이 보이는 사진을 추가해 주세요" - className="resize-y rounded-xl border border-admin-line bg-admin-base px-4 py-3 text-[14px] text-admin-ink outline-none focus:border-admin-accent" + className="resize-y rounded-xl border border-admin-line bg-admin-base px-4 py-3 text-[14px] mobile:text-base text-admin-ink outline-none focus:border-admin-accent" />
diff --git a/src/app/dashboard/club/team/page.tsx b/src/app/dashboard/club/team/page.tsx index 479e2e3..fa4c2fe 100644 --- a/src/app/dashboard/club/team/page.tsx +++ b/src/app/dashboard/club/team/page.tsx @@ -50,7 +50,7 @@ const STATE_CLASS: Record = { const CARD = 'flex flex-col gap-3 rounded-[20px] border border-admin-line-soft bg-admin-card p-[22px] shadow-admin' const FIELD = - 'resize-y rounded-xl border border-admin-line bg-admin-base px-4 py-3 text-[14px] text-admin-ink outline-none focus:border-admin-accent' + 'resize-y rounded-xl border border-admin-line bg-admin-base px-4 py-3 text-[14px] mobile:text-base text-admin-ink outline-none focus:border-admin-accent' /** * 팀 상세·완주 확정 (C 담당). 계산상 미충족이어도 확정할 수 있다 — 확정 전에 한 번 더 묻는다. diff --git a/src/app/dashboard/core-applications/page.tsx b/src/app/dashboard/core-applications/page.tsx index a136d9c..e2c6a12 100644 --- a/src/app/dashboard/core-applications/page.tsx +++ b/src/app/dashboard/core-applications/page.tsx @@ -384,7 +384,7 @@ export default function DashboardCoreApplicationsPage() {
- +
{['이름', '학과 · 학번', '지원 팀', '상태', '제출 시각'].map((label) => ( diff --git a/src/app/dashboard/core/attendance/page.tsx b/src/app/dashboard/core/attendance/page.tsx index f600107..aaff091 100644 --- a/src/app/dashboard/core/attendance/page.tsx +++ b/src/app/dashboard/core/attendance/page.tsx @@ -526,7 +526,7 @@ export default function DashboardCoreAttendancePage() { aria-label="추가할 일정 날짜" value={newDate} onChange={(e) => setNewDate(e.target.value)} - className="rounded-full border border-admin-line bg-admin-card px-3.5 py-1.5 text-[13px] text-admin-ink outline-none transition-colors duration-200 focus:border-admin-accent" + className="rounded-full border border-admin-line bg-admin-card px-3.5 py-1.5 text-[13px] mobile:text-base text-admin-ink outline-none transition-colors duration-200 focus:border-admin-accent" />
+
{['이름', '팀', '상태', '최종 수정'].map((label) => ( diff --git a/src/app/dashboard/events/applicants/page.tsx b/src/app/dashboard/events/applicants/page.tsx index cfc9d50..e926502 100644 --- a/src/app/dashboard/events/applicants/page.tsx +++ b/src/app/dashboard/events/applicants/page.tsx @@ -243,7 +243,7 @@ export default function EventApplicantsPage() { ) : ( applicants.map((applicant) => ( - - - - + + +
+ {applicant.name} {applicant.userId == null && ( (비로그인) @@ -252,9 +252,15 @@ export default function EventApplicantsPage() { (취소) )} {applicant.studentId}{formatMajorLabel(applicant.major)}{formatDate(applicant.appliedAt)} + {applicant.studentId} + + {formatMajorLabel(applicant.major)} + + {formatDate(applicant.appliedAt)} +
+
{['이름', '학과 · 학번', '지원 학기', '제출 시각', '전화번호', '회비'].map( diff --git a/src/app/dashboard/page.tsx b/src/app/dashboard/page.tsx index 4bfd737..4b11b25 100644 --- a/src/app/dashboard/page.tsx +++ b/src/app/dashboard/page.tsx @@ -252,7 +252,7 @@ export default function DashboardIndexPage() { value={query} onChange={(event) => setQuery(event.target.value)} placeholder="페이지 검색 ( / 키로 이동 )" - className="min-w-0 flex-1 border-0 bg-transparent text-[15px] text-admin-ink outline-none" + className="min-w-0 flex-1 border-0 bg-transparent text-[15px] mobile:text-base text-admin-ink outline-none" /> @@ -262,7 +262,7 @@ export default function DashboardIndexPage() {
+
+
{['ID', '이름', '이메일', '학과 · 학번', '권한', '팀'].map((label) => ( diff --git a/src/components/admin/dashboard/adminStyles.ts b/src/components/admin/dashboard/adminStyles.ts index 84a766c..249b606 100644 --- a/src/components/admin/dashboard/adminStyles.ts +++ b/src/components/admin/dashboard/adminStyles.ts @@ -9,23 +9,28 @@ export const ADMIN_PILL = 'flex items-center gap-2.5 rounded-full border border-admin-line bg-admin-card px-4 py-2.5' -/** 알약 안에 들어가는 네이티브 셀렉트 */ +/** + * 알약 안에 들어가는 네이티브 셀렉트. + * + * 입력 컨트롤은 모바일에서 16px 이상이어야 한다 — iOS Safari 는 그보다 작은 칸에 포커스하면 + * 화면을 확대하고 되돌리지 않는다. + */ export const ADMIN_PILL_SELECT = - 'cursor-pointer border-0 bg-transparent text-[15px] text-admin-ink outline-none' + 'cursor-pointer border-0 bg-transparent text-[15px] mobile:text-base text-admin-ink outline-none' /** 셀렉트 옵션 — 브라우저가 배경을 상속하지 않아 직접 준다 */ export const ADMIN_OPTION = 'bg-admin-card text-admin-ink' /** 표 안에서 쓰는 셀렉트 */ export const ADMIN_CELL_SELECT = - 'w-full min-w-[96px] cursor-pointer rounded-[10px] border border-admin-line bg-admin-card px-2.5 py-2 text-[14px] text-admin-ink outline-none transition-colors duration-200 hover:border-admin-accent disabled:cursor-not-allowed disabled:opacity-40' + 'w-full min-w-[96px] cursor-pointer rounded-[10px] border border-admin-line bg-admin-card px-2.5 py-2 text-[14px] mobile:text-base text-admin-ink outline-none transition-colors duration-200 hover:border-admin-accent disabled:cursor-not-allowed disabled:opacity-40' /** 검색 필드를 감싸는 라벨 */ export const ADMIN_SEARCH_FIELD = 'flex min-w-0 flex-1 basis-[300px] items-center gap-2.5 rounded-full border border-admin-line bg-admin-card px-4 py-2.5 shadow-admin transition duration-[250ms] focus-within:border-admin-accent focus-within:shadow-admin-ring' export const ADMIN_SEARCH_INPUT = - 'min-w-0 flex-1 border-0 bg-transparent text-[15px] text-admin-ink outline-none' + 'min-w-0 flex-1 border-0 bg-transparent text-[15px] mobile:text-base text-admin-ink outline-none' /** 강조 버튼 (저장 등) */ export const ADMIN_ACCENT_BUTTON = @@ -39,9 +44,13 @@ export const ADMIN_ACCENT_BUTTON_SM = export const ADMIN_GHOST_BUTTON = 'whitespace-nowrap rounded-full border border-admin-line px-[18px] py-2 text-[13px] text-admin-ink-muted transition-colors duration-200 hover:border-admin-accent hover:text-admin-ink disabled:cursor-not-allowed disabled:opacity-40' -/** 표를 감싸는 카드 — `overflow-hidden` 이 라운드를 지킨다 */ +/** + * 표를 감싸는 카드. `overflow-x-auto` 가 라운드를 지키면서 좁은 화면에서 가로 스크롤을 준다 — + * `overflow-hidden` 이던 때는 모바일에서 오른쪽 열이 잘린 채 밀어도 움직이지 않았다. + * 안의 표는 `min-w-max` 등으로 폭을 지켜야 열이 찌그러지지 않는다. + */ export const ADMIN_TABLE_CARD = - 'overflow-hidden rounded-[20px] border border-admin-line-soft bg-admin-card shadow-admin' + 'overflow-x-auto rounded-[20px] border border-admin-line-soft bg-admin-card shadow-admin' /** * 표 헤더 셀. `top-0` 이어야 한다 — 68px 을 주면 첫 행과 겹친다. diff --git a/src/components/board/AttachmentList.tsx b/src/components/board/AttachmentList.tsx index 443d70f..c4a109f 100644 --- a/src/components/board/AttachmentList.tsx +++ b/src/components/board/AttachmentList.tsx @@ -59,9 +59,10 @@ export function AttachmentList({ attachments }: AttachmentListProps) { href={href ?? '#'} target="_blank" rel="noreferrer" - className="flex items-center gap-2 overflow-hidden text-ellipsis whitespace-nowrap rounded-[10px] bg-[rgba(240,234,228,0.06)] px-4 py-[9px] text-[15px] text-tag-info underline transition-colors hover:bg-[rgba(240,234,228,0.11)]" + className="flex items-center gap-2 rounded-[10px] bg-[rgba(240,234,228,0.06)] px-4 py-[9px] text-[15px] text-tag-info underline transition-colors hover:bg-[rgba(240,234,228,0.11)]" > - {attachment.url} + {/* flex 컨테이너에 직접 둔 텍스트에는 말줄임이 안 걸려 긴 URL 이 오른쪽 여백까지 잘린다. */} + {attachment.url} ) diff --git a/src/components/board/boardMenus.ts b/src/components/board/boardMenus.ts index fb66386..08e1ff6 100644 --- a/src/components/board/boardMenus.ts +++ b/src/components/board/boardMenus.ts @@ -8,8 +8,8 @@ import type { GdgMenuLink } from '@/components/ui/design-system' * 게시판이 늘어날 때 여기 한 곳만 고치면 8개 페이지가 함께 맞는다. */ export const BOARD_MENUS: GdgMenuLink[] = [ - { label: '공지사항', url: '/board/notices/' }, - { label: '행사게시판', url: '/board/events/' }, - { label: '자유게시판', url: '/board/free/' }, + { label: '공지사항', url: '/board/notices/', shortLabel: '공지' }, + { label: '행사게시판', url: '/board/events/', shortLabel: '행사' }, + { label: '자유게시판', url: '/board/free/', shortLabel: '자유' }, { label: '소모임', url: '/club/' } ] diff --git a/src/components/club/admin/ClubTermCard.tsx b/src/components/club/admin/ClubTermCard.tsx index 49d4fa9..7d7f284 100644 --- a/src/components/club/admin/ClubTermCard.tsx +++ b/src/components/club/admin/ClubTermCard.tsx @@ -16,7 +16,7 @@ import { cn } from '@/utils/cn' const CARD = 'flex flex-col gap-3 rounded-[20px] border border-admin-line-soft bg-admin-card p-[22px] shadow-admin' const FIELD = - 'rounded-xl border border-admin-line bg-admin-base px-4 py-3 text-[14px] text-admin-ink outline-none focus:border-admin-accent' + 'rounded-xl border border-admin-line bg-admin-base px-4 py-3 text-[14px] mobile:text-base text-admin-ink outline-none focus:border-admin-accent' /** * 기수 설정 (C 담당) — `/dashboard/club/leaders` 오른쪽 칸. 이름과 참석 비율만 있다. 활동 기간은 소모임마다 둔다. diff --git a/src/components/eventApplication/QuestionEditor.tsx b/src/components/eventApplication/QuestionEditor.tsx index 4309cfe..d02658c 100644 --- a/src/components/eventApplication/QuestionEditor.tsx +++ b/src/components/eventApplication/QuestionEditor.tsx @@ -20,7 +20,7 @@ import { } from '@/types/eventApplication' const INPUT = - 'w-full rounded-[10px] border border-admin-line bg-admin-card px-3 py-2 text-[14px] text-admin-ink outline-none transition-colors duration-200 focus:border-admin-accent disabled:cursor-not-allowed disabled:opacity-40' + 'w-full rounded-[10px] border border-admin-line bg-admin-card px-3 py-2 text-[14px] mobile:text-base text-admin-ink outline-none transition-colors duration-200 focus:border-admin-accent disabled:cursor-not-allowed disabled:opacity-40' const LABEL = 'shrink-0 whitespace-nowrap text-[12px] tracking-[0.06em] text-admin-ink-dim' diff --git a/src/components/landing/onboarding/LandingHeader.tsx b/src/components/landing/onboarding/LandingHeader.tsx index d13e0ac..e4b402c 100644 --- a/src/components/landing/onboarding/LandingHeader.tsx +++ b/src/components/landing/onboarding/LandingHeader.tsx @@ -3,7 +3,7 @@ import Image from 'next/image' import { useAuth } from '@/hooks/useAuth' -import { hasAtLeast } from '@/utils/auth/role' +import { canOpenDashboard, hasAtLeast } from '@/utils/auth/role' import gdgocPcLogo from '@public/icons/gdgocIcon/pc.svg' @@ -39,9 +39,10 @@ export default function LandingHeader() { const isLoggedIn = Boolean(user) // 콘텐츠 관리 화면과 같은 기준이다 (dashboard/landing/layout.tsx). const canEditContent = hasAtLeast(user?.userRole, 'LEAD') + const canOpenAdmin = canOpenDashboard(user?.userRole) return ( -
+
{ @@ -66,12 +67,26 @@ export default function LandingHeader() { -
- - {/* - 서랍은 header 밖에 둔다. header 에 backdrop-blur 가 걸려 있는데 - backdrop-filter 는 fixed 자손의 컨테이닝 블록이 되어, 안에 두면 이 상자가 - 뷰포트가 아니라 헤더 높이(64px)로 잡힌다 — 서랍이 그만큼만 열린다. - - 상자 자체는 화면을 넘지 않고, 서랍은 그 안에서 absolute 로 밀어낸다. - 닫힌 서랍을 fixed 로 화면 밖에 세우면 문서 폭이 늘어나 가로 스크롤이 생기는데, - overflow-hidden 인 이 상자가 그것도 같이 막는다. - */} -
-
setIsMenuOpen(false)} - /> - -
+ + - + ) + })} +
- +
) } diff --git a/src/components/ui/design-system/GdgSiteNav.types.ts b/src/components/ui/design-system/GdgSiteNav.types.ts index 804bc4e..786107b 100644 --- a/src/components/ui/design-system/GdgSiteNav.types.ts +++ b/src/components/ui/design-system/GdgSiteNav.types.ts @@ -1,4 +1,6 @@ export type GdgMenuLink = { label: string url: string + /** 모바일 탭 줄처럼 폭이 좁은 곳에서 쓰는 짧은 이름 */ + shortLabel?: string } diff --git a/src/utils/auth/role.ts b/src/utils/auth/role.ts index 381eac5..1879263 100644 --- a/src/utils/auth/role.ts +++ b/src/utils/auth/role.ts @@ -12,6 +12,15 @@ export const hasAtLeast = ( minimumRole: UserRoleValue ): boolean => rankOf(currentRole) >= rankOf(minimumRole) +/** + * 운영진 대시보드(`/dashboard`)에 들어갈 수 있는 최소 역할. + * 대시보드 가드와 헤더의 「운영」 버튼이 같이 본다 — 따로 적으면 버튼은 보이는데 열면 막히는 일이 생긴다. + */ +export const DASHBOARD_MIN_ROLE: UserRoleValue = 'CORE' + +export const canOpenDashboard = (currentRole: string | undefined | null): boolean => + hasAtLeast(currentRole, DASHBOARD_MIN_ROLE) + /** * 부원 정보를 고칠 수 있는가 — 서버 LEAD_OR_HR_RULE 과 같은 규칙이다. * CORE 는 조회만 되므로 쓰기 버튼은 이 판정으로 막는다.