Skip to content
16 changes: 14 additions & 2 deletions src/app/[groupId]/tasklist/_components/task-list-date-picker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,21 @@ const TaskListDatePicker = ({
setCurrentSunday(sunday);
setWeek(getWeek(sunday));
setSelectedDate(date);
setShowCalendar(false);
};

const handleChangeDay = (e: ChangeEvent<HTMLInputElement>) => {
if (!currentSunday) return;
const selectedDay = Number(e.target.value);
setDay(e.target.value);

const weekDays = week || [];
const dayIndex = weekDays.indexOf(selectedDay);

if (dayIndex === -1) return;

const newDate = new Date(currentSunday);
newDate.setDate(Number(e.target.value));
newDate.setDate(currentSunday.getDate() + dayIndex);
Copy link
Contributor

Choose a reason for hiding this comment

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

PR 설명에 텍스트가 표출이 안된다고 하셨는데
혹시 어떤 텍스트가 표출이 안되는 문제인건가요??

Copy link
Member Author

Choose a reason for hiding this comment

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

PR 설명에 텍스트가 표출이 안된다고 하셨는데 혹시 어떤 텍스트가 표출이 안되는 문제인건가요??

스크린샷 2025-11-30 13 36 33

해당 이미지의 2025년 11월 텍스트가 캘린더에서 12월을 선택해도 여전히 11월로 유지되고있었습니다!

Copy link
Contributor

Choose a reason for hiding this comment

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

아 이걸 확인을 못했었네요.. 감사합니다

setSelectedDate(newDate);
};

Expand Down Expand Up @@ -114,7 +122,11 @@ const TaskListDatePicker = ({
<Button
variant="none"
className="h-6 w-6 rounded-full bg-gray-50"
onClick={() => setShowCalendar((prevState) => !prevState)}
onMouseDown={(e) => {
e.preventDefault();
e.stopPropagation();
setShowCalendar((prevState) => !prevState);
}}
>
<Icon icon="calendar" className="h-3 w-3 text-gray-800" />
</Button>
Expand Down
16 changes: 10 additions & 6 deletions src/app/addteam/_components/add-team-contents.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"use client";

import { useCallback } from "react";
import { useForm } from "react-hook-form";
import {
ProfileEdit,
Expand Down Expand Up @@ -35,12 +36,15 @@ const AddTeamContents = () => {
isUploading: isImageUploading,
} = useProfileImageManager();

const onSubmit = (data: { name: string; image?: string }) => {
createGroup({
name: data.name,
...(profileImage && { image: profileImage }),
});
};
const onSubmit = useCallback(
(data: { name: string; image?: string }) => {
createGroup({
name: data.name,
...(profileImage && { image: profileImage }),
});
},
[createGroup, profileImage]
);

return (
<div className="flex flex-col items-start gap-8 px-[21px] pb-[74.5px] pt-[52.5px] tablet:gap-10 tablet:px-[45px] tablet:pb-[64px] tablet:pt-[61px]">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,9 @@ const ArticleHeader = ({ article }: ArticleHeaderProps) => {
<>
<div className="flex w-full flex-col gap-4 pb-[16px] tablet:pb-[28px]">
<div className="flex items-center justify-between">
<h2 className="text-xl font-bold text-blue-700">{article.title}</h2>
<h2 className="break-all text-xl font-bold text-blue-700">
{article.title}
</h2>
{isWriter && (
<Dropdown
trigger={
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
import usePatchArticle from "@/hooks/api/articles/use-patch-article";
import { Article } from "@/types/article";
import { filterProfanity } from "@/utils/profanityFilter";
import { MAX_ARTICLE_TITLE_LENGTH } from "@/app/boards/_constants/article";

interface ArticleEditContentsProps {
article: Article;
Expand All @@ -29,6 +30,7 @@ const ArticleEditContents = ({ article }: ArticleEditContentsProps) => {
register,
handleSubmit,
formState: { errors },
watch,
} = useForm<EditFormData>({
defaultValues: {
title: article.title,
Expand All @@ -39,6 +41,7 @@ const ArticleEditContents = ({ article }: ArticleEditContentsProps) => {
const [images, setImages] = useState<string[]>(
article.image ? [article.image] : []
);
const titleValue = watch("title") || "";

const handleImagesChange = useCallback((imgs: string[]) => {
setImages(imgs);
Expand Down Expand Up @@ -76,15 +79,32 @@ const ArticleEditContents = ({ article }: ArticleEditContentsProps) => {
>
<h2 className="pb-[32px] text-xl font-bold text-blue-700">게시글 수정</h2>

<div className="flex flex-col items-start gap-2 pb-[24px] tablet:gap-3 tablet:pb-[32px]">
<div className="flex w-full flex-col items-start gap-2 pb-[24px] tablet:gap-3 tablet:pb-[32px]">
<div className="flex gap-[6px]">
<span className="text-lg font-bold text-blue-700">제목</span>
<span className="text-red-200">*</span>
</div>
<InputBox
placeholder="제목을 입력해주세요."
{...register("title", { required: "제목은 필수입니다" })}
/>
<div className="relative flex w-full items-center">
<InputBox
placeholder="제목을 입력해주세요."
{...register("title", { required: "제목은 필수입니다" })}
maxLength={MAX_ARTICLE_TITLE_LENGTH}
/>
</div>
{titleValue.length > 0 && (
<div className="w-full text-right">
<span
className={cn(
"text-xs",
titleValue.length === MAX_ARTICLE_TITLE_LENGTH
? "text-red-500"
: "text-gray-500"
)}
>
{titleValue.length}/{MAX_ARTICLE_TITLE_LENGTH}
</span>
</div>
)}
{errors.title && (
<span className="text-sm text-red-500">{errors.title.message}</span>
)}
Expand Down
11 changes: 7 additions & 4 deletions src/app/boards/_components/boards-header/boards-header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,19 @@

import cn from "@/utils/clsx";
import { TextInput, Button, Icon } from "@/components/index";
import { ChangeEvent } from "react";
import { useCallback, ChangeEvent } from "react";

const BoardsHeader = ({
onSearch,
}: {
onSearch: (keyword: string) => void;
}) => {
const handleSearch = (e: ChangeEvent<HTMLInputElement>) => {
onSearch(e.target.value);
};
const handleSearch = useCallback(
(e: ChangeEvent<HTMLInputElement>) => {
onSearch(e.target.value);
},
[onSearch]
);

return (
<div
Expand Down
6 changes: 6 additions & 0 deletions src/app/boards/_constants/article.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/**
* 게시글 관련 상수
*/

/** 게시글 제목 최대 길이 */
export const MAX_ARTICLE_TITLE_LENGTH = 200;
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
} from "@/components/index";
import { usePostArticle } from "@/hooks/api/articles/use-post-article";
import { filterProfanity } from "@/utils/profanityFilter";
import { MAX_ARTICLE_TITLE_LENGTH } from "@/app/boards/_constants/article";

interface WriteFormData {
title: string;
Expand All @@ -24,9 +25,11 @@ const ArticleWriteContents = () => {
register,
handleSubmit,
formState: { errors },
watch,
} = useForm<WriteFormData>();
const { mutate, isPending } = usePostArticle();
const [images, setImages] = useState<string[]>([]);
const titleValue = watch("title") || "";

const handleImagesChange = useCallback((images: string[]) => {
setImages(images);
Expand Down Expand Up @@ -61,15 +64,32 @@ const ArticleWriteContents = () => {
>
<h2 className="pb-[32px] text-xl font-bold text-blue-700">게시글 쓰기</h2>

<div className="flex flex-col items-start gap-2 pb-[24px] tablet:gap-3 tablet:pb-[32px]">
<div className="flex w-full flex-col items-start gap-2 pb-[24px] tablet:gap-3 tablet:pb-[32px]">
<div className="flex gap-[6px]">
<span className="text-lg font-bold text-blue-700">제목</span>
<span className="text-red-200">*</span>
</div>
<InputBox
placeholder="제목을 입력해주세요."
{...register("title", { required: "제목은 필수입니다" })}
/>
<div className="relative flex w-full items-center">
<InputBox
placeholder="제목을 입력해주세요."
{...register("title", { required: "제목은 필수입니다" })}
maxLength={MAX_ARTICLE_TITLE_LENGTH}
/>
</div>
{titleValue.length > 0 && (
<div className="w-full text-right">
<span
className={cn(
"text-xs",
titleValue.length === MAX_ARTICLE_TITLE_LENGTH
? "text-red-500"
: "text-gray-500"
)}
>
{titleValue.length}/{MAX_ARTICLE_TITLE_LENGTH}
</span>
</div>
)}
{errors.title && (
<span className="text-sm text-red-500">{errors.title.message}</span>
)}
Expand Down
31 changes: 18 additions & 13 deletions src/components/input-box/input-box.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,22 +37,27 @@ const InputBox = ({
...rest
}: InputBoxProps) => {
return (
<textarea
id={id}
ref={ref}
placeholder={placeholder}
<div
className={cn(
"resize-none rounded-xl border border-gray-300 px-4 py-3 font-normal focus:border-blue-400 focus:outline-none",
"text-md placeholder:text-sm",
"tablet:text-lg tablet:placeholder:text-md",
"border-blue-400 placeholder-shown:border-gray-300 focus:border-blue-200",
"break-words",
"overflow-hidden rounded-xl border border-gray-300 focus-within:border-blue-200 has-[:placeholder-shown]:border-gray-300",
width,
height,
className
height
)}
{...rest}
/>
>
<textarea
id={id}
ref={ref}
placeholder={placeholder}
className={cn(
"h-full w-full resize-none bg-transparent px-4 py-3 font-normal focus:outline-none",
"text-md placeholder:text-sm",
"tablet:text-lg tablet:placeholder:text-md",
"break-words",
className
)}
{...rest}
/>
</div>
);
};

Expand Down
28 changes: 23 additions & 5 deletions src/components/input-reply/input-reply.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import cn from "@/utils/clsx";
import Button from "../button/button";
import Icon from "../icon/Icon";
import TextareaAutosize from "react-textarea-autosize";
import { pl } from "react-day-picker/locale";
import { MAX_COMMENT_LENGTH } from "@/constants/comment";

/**
* @author junyeol
Expand All @@ -24,8 +24,11 @@ const InputReply = ({
disabled = false,
}: InputReplyProps) => {
const [value, setValue] = useState("");

const handleChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
setValue(e.target.value);
if (e.target.value.length <= MAX_COMMENT_LENGTH) {
setValue(e.target.value);
}
};

const handleSubmit = () => {
Expand All @@ -37,27 +40,42 @@ const InputReply = ({

return (
<div className="flex w-full max-w-[780px] flex-col border-y-2 border-gray-300 py-3">
<div className="gap-6 pl-3 pr-3 flex-center">
<div className="flex items-center gap-6 pl-3 pr-3">
<TextareaAutosize
placeholder={placeholder}
value={value}
onChange={handleChange}
disabled={disabled}
className="w-full max-w-[708px] resize-none text-xs text-blue-700 placeholder:text-gray-800 focus:outline-none tablet:text-md"
maxLength={MAX_COMMENT_LENGTH}
className="w-full resize-none text-xs text-blue-700 placeholder:text-gray-800 focus:outline-none tablet:text-md"
minRows={1}
/>
<Button
variant="none"
disabled={!value.trim() || disabled}
onClick={handleSubmit}
className={cn(
"h-[24px] w-[24px] flex-shrink-0 rounded-full",
"h-[24px] w-[24px] flex-shrink-0 self-end rounded-full",
value ? "bg-blue-100" : "bg-gray-800"
)}
>
<Icon icon="upArrow" className="h-4 w-4" />
</Button>
</div>
{value.length > 0 && (
<div className="pr-3 pt-1 text-right">
<span
className={cn(
"text-xs",
value.length === MAX_COMMENT_LENGTH
? "text-red-500"
: "text-gray-500"
)}
>
{value.length}/{MAX_COMMENT_LENGTH}
</span>
</div>
)}
</div>
);
};
Expand Down
32 changes: 25 additions & 7 deletions src/components/reply/reply.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import TextareaAutosize from "react-textarea-autosize";
import DefaultProfile from "@/assets/icons/ic-user.svg";
import { toDotDateString } from "@/utils/date-util";
import { useGetUserInfoQuery } from "@/hooks/api/user/use-get-user-info-query";
import { MAX_COMMENT_LENGTH } from "@/constants/comment";

interface CommentProps {
comment: Comment;
Expand Down Expand Up @@ -95,12 +96,29 @@ const Reply = ({ comment, onEdit, onDelete }: CommentProps) => {

{isEditing ? (
<div className="flex flex-col gap-2">
<TextareaAutosize
value={editedContent}
onChange={(e) => setEditedContent(e.target.value)}
className="w-full resize-none rounded-lg border border-blue-400 px-2 py-2 text-md leading-relaxed focus:outline-none"
minRows={3}
/>
<div className="relative">
<TextareaAutosize
value={editedContent}
onChange={(e) => {
if (e.target.value.length <= MAX_COMMENT_LENGTH) {
setEditedContent(e.target.value);
}
}}
maxLength={MAX_COMMENT_LENGTH}
className="w-full resize-none rounded-lg border border-blue-400 px-2 pb-6 pt-2 text-md leading-relaxed focus:outline-none"
minRows={3}
/>
<span
className={cn(
"absolute bottom-4 right-2 text-xs",
editedContent.length === MAX_COMMENT_LENGTH
? "text-red-500"
: "text-gray-500"
)}
>
{editedContent.length}/{MAX_COMMENT_LENGTH}
</span>
</div>
<div className="flex gap-2">
<Button
onClick={handleEdit}
Expand All @@ -119,7 +137,7 @@ const Reply = ({ comment, onEdit, onDelete }: CommentProps) => {
</div>
</div>
) : (
<p className="w-full text-md leading-relaxed text-gray-800 tablet:max-w-[464px] pc:max-w-[704px]">
<p className="w-full whitespace-pre-wrap break-words text-md leading-relaxed text-gray-800 tablet:max-w-[464px] pc:max-w-[665px]">
{editedContent}
</p>
)}
Expand Down
6 changes: 6 additions & 0 deletions src/constants/comment.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/**
* 댓글 관련 상수
*/

/** 댓글 내용 최대 길이 */
export const MAX_COMMENT_LENGTH = 255;
Loading