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
2 changes: 1 addition & 1 deletion src/app/(private)/[clubId]/admin/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export default async function AdminLayout({
<div data-admin className="fixed inset-0 flex flex-col">
<div className="flex flex-1 overflow-hidden">
<LNB />
<main className="bg-background flex-1 overflow-x-hidden overflow-y-auto [scrollbar-gutter:stable]">
<main className="bg-background min-w-0 flex-1 overflow-x-auto overflow-y-auto [scrollbar-gutter:stable]">
<Header />
{children}
</main>
Expand Down
8 changes: 5 additions & 3 deletions src/components/admin/club-info/AdminInfoCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,15 @@ function AdminInfoCard({
return (
<div
className={cn(
'bg-container-neutral flex w-full max-w-[942px] flex-col rounded-lg p-500 px-600 shadow-sm',
'bg-container-neutral flex w-full max-w-[942px] flex-col rounded-lg py-500 shadow-sm',
className,
)}
{...props}
>
<h3 className="typo-sub1 text-text-normal">{title}</h3>
<div className={cn('mt-400 flex flex-col gap-300', titleGapClassName, contentClassName)}>
<h3 className="typo-sub1 text-text-normal px-600">{title}</h3>
<div
className={cn('mt-400 flex flex-col gap-300 px-600', titleGapClassName, contentClassName)}
>
{children}
</div>
</div>
Expand Down
46 changes: 33 additions & 13 deletions src/components/admin/club-info/ClubInfoBasicSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,21 +40,41 @@ function ClubInfoBasicSection({
</FieldBlock>

<FieldBlock label="동아리 이름">
<Input
value={clubName}
onChange={(e) => onNameChange(e.target.value)}
className="bg-container-neutral-alternative rounded-sm border-transparent px-400 py-300"
/>
<div className="flex flex-col gap-200">
<Input
value={clubName}
onChange={(e) => onNameChange(e.target.value)}
maxLength={30}
className="bg-container-neutral-alternative rounded-sm border-transparent px-400 py-300"
/>
<div className="flex min-h-4 items-start justify-end gap-200">
<span className="typo-caption2 text-text-alternative shrink-0 text-right">
{clubName.length}/30
</span>
</div>
</div>
</FieldBlock>

<FieldBlock label="동아리 소개" helper="최대 30자" error={descriptionError}>
<Input
value={description}
onChange={(e) => onDescriptionChange(e.target.value)}
maxLength={30}
placeholder="동아리를 소개하는 짧은 글을 작성해주세요"
className="bg-container-neutral-alternative rounded-sm border-transparent px-400 py-300"
/>
<FieldBlock label="동아리 소개">
<div className="flex flex-col gap-200">
<Input
value={description}
onChange={(e) => onDescriptionChange(e.target.value)}
maxLength={30}
placeholder="동아리를 소개하는 짧은 글을 작성해주세요"
className="bg-container-neutral-alternative rounded-sm border-transparent px-400 py-300"
/>
<div className="flex min-h-4 items-start justify-between gap-200">
{descriptionError ? (
<span className="typo-caption2 text-state-error">{descriptionError}</span>
) : (
<span />
)}
<span className="typo-caption2 text-text-alternative shrink-0 text-right">
{description.length}/30
</span>
</div>
</div>
</FieldBlock>
</div>
</AdminInfoCard>
Expand Down
2 changes: 1 addition & 1 deletion src/components/admin/club-info/ClubInfoContactSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ function ClubInfoContactSection({
title="연락처"
titleGapClassName="mt-[58px]"
contentClassName="gap-0"
className="pb-[70px]"
className="pb-[40px]"
>
<div className="flex flex-col gap-400">
<FieldBlock label="대표 전화번호" error={phoneError}>
Expand Down
2 changes: 1 addition & 1 deletion src/components/admin/club-info/ClubInfoPageContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ function ClubInfoPageContent({ schoolNames }: ClubInfoPageContentProps) {
);

return (
<div className="flex flex-col">
<div className="flex w-full flex-col overflow-x-auto">
{isEditMode && (
<ClubInfoTopBar
className="sticky top-0 z-10 -mt-15"
Expand Down
4 changes: 2 additions & 2 deletions src/components/admin/club-info/FieldBlock.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ interface FieldBlockProps {

function FieldBlock({ label, helper, error, children }: FieldBlockProps) {
return (
<div className="flex flex-col gap-300">
<span className="typo-caption1 text-text-normal">{label}</span>
<div className="flex flex-col gap-400">
<span className="typo-caption1 text-text-normal px-400">{label}</span>
{children}
{error ? (
<span className="typo-caption2 text-state-error">{error}</span>
Expand Down
2 changes: 1 addition & 1 deletion src/components/attendance/AttendanceHistoryContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ function AttendanceHistoryContent({ summary, errorMessage }: AttendanceHistoryCo
<div className="bg-container-neutral flex flex-col gap-400 rounded-lg p-400">
<div className="flex gap-200">
<StatBox label="정기 모임" value={`${total ?? 0}회`} />
<StatBox label="출석" value={`${attendanceCount ?? 0}회`} />
<StatBox label="세션" value={`${attendanceCount ?? 0}회`} />
<StatBox label="결석" value={`${absenceCount ?? 0}회`} />
</div>

Expand Down
40 changes: 36 additions & 4 deletions src/components/auth/hub/CreateClubForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ function CreateClubForm({ schoolNames, schoolLoadError = false }: CreateClubForm

const contactType = useWatch({ control, name: 'contactType' });
const email = useWatch({ control, name: 'email' });
const clubName = useWatch({ control, name: 'name' });
const description = useWatch({ control, name: 'description' });
const hasValidEmail = Boolean(email?.trim()) && !errors.email;
const isEmailContactDisabled = !hasValidEmail;

Expand Down Expand Up @@ -143,19 +145,49 @@ function CreateClubForm({ schoolNames, schoolLoadError = false }: CreateClubForm
</FormFieldWrapper>

{/* 동아리 이름 */}
<FormFieldWrapper label="동아리 이름" error={errors.name?.message}>
<Input {...register('name')} clearable className="typo-body1 rounded-lg px-400 py-300" />
<FormFieldWrapper label="동아리 이름">
<Input
{...register('name')}
clearable
clearButtonClassName="text-icon-alternative"
maxLength={30}
className="typo-body1 rounded-lg px-400 py-300"
/>
<div className="grid min-h-4 grid-cols-[minmax(0,1fr)_auto] items-start gap-200">
<div className="min-w-0">
{errors.name?.message ? (
<span className="typo-caption2 text-state-error block truncate">
{errors.name.message}
</span>
) : null}
</div>
<span className="typo-caption2 text-text-alternative shrink-0 text-right">
{(clubName ?? '').length}/30
</span>
</div>
</FormFieldWrapper>

{/* 동아리 소개 */}
<FormFieldWrapper label="동아리 소개" error={errors.description?.message}>
<FormFieldWrapper label="동아리 소개">
<Input
{...register('description')}
maxLength={30}
clearable
clearButtonClassName="text-icon-alternative"
className="typo-body1 rounded-lg px-400 py-300"
/>
<span className="typo-caption2 text-text-alternative">30자 제한</span>
<div className="grid min-h-4 grid-cols-[minmax(0,1fr)_auto] items-start gap-200">
<div className="min-w-0">
{errors.description?.message ? (
<span className="typo-caption2 text-state-error block truncate">
{errors.description.message}
</span>
) : null}
</div>
<span className="typo-caption2 text-text-alternative shrink-0 text-right">
{(description ?? '').length}/30
</span>
</div>
Comment thread
coderabbitai[bot] marked this conversation as resolved.
</FormFieldWrapper>

{/* 동아리 기수 */}
Expand Down
12 changes: 11 additions & 1 deletion src/components/ui/Input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,19 @@ interface InputProps extends InputHTMLAttributes<HTMLInputElement> {
clearable?: boolean;
error?: boolean;
wrapperClassName?: string;
clearButtonClassName?: string;
ref?: React.Ref<HTMLInputElement>;
}

function Input({ className, clearable, error, wrapperClassName, ref, ...props }: InputProps) {
function Input({
className,
clearable,
error,
wrapperClassName,
clearButtonClassName,
ref,
...props
}: InputProps) {
const hasTypo = className?.split(' ').some((c) => c.startsWith('typo-'));
const typoClass = hasTypo ? undefined : 'typo-body2';
const innerRef = useRef<HTMLInputElement>(null);
Expand Down Expand Up @@ -99,6 +108,7 @@ function Input({ className, clearable, error, wrapperClassName, ref, ...props }:
'flex items-center',
'text-icon-normal',
'cursor-pointer transition-colors',
clearButtonClassName,
)}
aria-label="입력 내용 지우기"
>
Expand Down
7 changes: 3 additions & 4 deletions src/hooks/home/useHomeGuard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,12 @@
import { useEffect, useSyncExternalStore } from 'react';
import { useRouter } from 'next/navigation';
import { isAxiosError } from 'axios';
import { useClubActions, useClubId, useClubName, useClubStore } from '@/stores/useClubStore';
import { useClubActions, useClubId, useClubStore } from '@/stores/useClubStore';
import { useHomeQuery } from './useHomeQuery';

export function useHomeGuard() {
const router = useRouter();
const clubId = useClubId();
const clubName = useClubName();
const { reset } = useClubActions();
const { error } = useHomeQuery();

Expand All @@ -21,11 +20,11 @@ export function useHomeGuard() {

useEffect(() => {
if (!hydrated) return;
if (!clubId || !clubName) {
if (!clubId) {
reset();
router.replace('/hub');
}
}, [hydrated, clubId, clubName, reset, router]);
}, [hydrated, clubId, reset, router]);

useEffect(() => {
if (isAxiosError(error) && error.response?.status === 404) {
Expand Down
Loading