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 apps/mobile/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "@layer/mobile",
"main": "expo-router/entry",
"types": "./bridge/native.ts",
"version": "1.0.2",
"version": "2.0.3",
"scripts": {
"start": "expo start",
"reset-project": "node ./scripts/reset-project.js",
Expand Down
2 changes: 1 addition & 1 deletion apps/web/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@layer/web",
"private": true,
"version": "2.0.0",
"version": "2.0.3",
"type": "module",
"scripts": {
"dev": "vite",
Expand Down
12 changes: 9 additions & 3 deletions apps/web/src/app/desktop/component/home/RetrospectCard/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ import { PATHS } from "@layer/shared";
import { useFunnelModal } from "@/hooks/useFunnelModal";
import { Prepare } from "../../retrospectWrite/prepare";
import TemplateCardManageToggleMenu from "@/component/retrospect/template/card/TemplateCardManageToggleMenu";
import { isSpaceLeader } from "@/utils/userUtil";
import { useAtomValue } from "jotai";
import { currentSpaceState } from "@/store/space/spaceAtom";

interface RetrospectCardProps {
retrospect: Retrospect;
Expand All @@ -20,6 +23,9 @@ export default function RetrospectCard({ retrospect, spaceId }: RetrospectCardPr
const navigate = useNavigate();
const [searchParams] = useSearchParams();
const { openFunnelModal } = useFunnelModal();
const currentSelectedSpace = useAtomValue(currentSpaceState);
const { leader } = currentSelectedSpace || {};
const isLeader = isSpaceLeader(leader?.id);

const {
spaceId: retrospectSpaceId,
Expand Down Expand Up @@ -70,7 +76,7 @@ export default function RetrospectCard({ retrospect, spaceId }: RetrospectCardPr
display: flex;
flex-direction: column;
width: 29.6rem;
height: fit-content;
height: 13.8rem;
max-height: 13.8rem;
padding: 1.6rem;
background-color: white;
Expand Down Expand Up @@ -98,7 +104,7 @@ export default function RetrospectCard({ retrospect, spaceId }: RetrospectCardPr
`}
>
<ProceedingTextBox writeStatus={writeStatus} analysisStatus={analysisStatus} />
<TemplateCardManageToggleMenu retrospect={retrospect} />
<TemplateCardManageToggleMenu retrospect={retrospect} isLeader={isLeader} />
</div>

{/* ---------- 제목 ---------- */}
Expand Down Expand Up @@ -134,7 +140,7 @@ export default function RetrospectCard({ retrospect, spaceId }: RetrospectCardPr
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 0.8rem;
margin-top: auto;
`}
>
<Typography variant="body12SemiBold" color="gray500">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,28 +14,43 @@ import { useToast } from "@/hooks/useToast";
import { DESIGN_TOKEN_COLOR } from "@/style/designTokens";

type AddQuestionViewProps = {
onAddQuestion: (content: string) => void;
onAddMultipleQuestions: (contents: string[]) => void;
onAddQuestions: (contents: string[]) => void;
maxCount: number;
};

export default function AddQuestionView({ onAddQuestion, onAddMultipleQuestions, maxCount }: AddQuestionViewProps) {
export default function AddQuestionView({ onAddQuestions, maxCount }: AddQuestionViewProps) {
const { toast } = useToast();
const { tabs, curTab, selectTab } = useTabs(["직접작성", "추천질문"] as const);
const { value: customQuestion, handleInputChange: handleCustomChange, resetInput } = useInput();
const { tabs: categoryTabs, curTab: curCategoryTab, selectTab: selectCategoryTab } = useTabs(QUESTION_TYPES);
const { selectedValues, isChecked, toggle } = useCheckBox();
const { selectedValues, isChecked, toggle, resetChecked } = useCheckBox();

const handleDirectAdd = () => {
if (customQuestion.trim()) {
onAddQuestion(customQuestion);
const handleAddQuestion = () => {
const hasCustomQuestion = customQuestion.trim().length > 0;
const hasSelectedQuestions = selectedValues.length > 0;

if (!hasCustomQuestion && !hasSelectedQuestions) {
return;
}

const questionsToAdd: string[] = [];

if (hasCustomQuestion) {
questionsToAdd.push(customQuestion.trim());
}

if (hasSelectedQuestions) {
questionsToAdd.push(...selectedValues);
}

onAddQuestions(questionsToAdd);

if (hasCustomQuestion) {
resetInput();
}
};

const handleRecommendedAdd = () => {
if (selectedValues.length > 0) {
onAddMultipleQuestions(selectedValues);
if (hasSelectedQuestions) {
resetChecked();
}
};

Expand Down Expand Up @@ -130,7 +145,7 @@ export default function AddQuestionView({ onAddQuestion, onAddMultipleQuestions,
padding: 0;
`}
>
<ButtonProvider.Primary onClick={curTab === "직접작성" ? handleDirectAdd : handleRecommendedAdd}>
<ButtonProvider.Primary onClick={handleAddQuestion} disabled={customQuestion.trim().length === 0 && selectedValues.length === 0}>
{selectedValues.length > 0 ? (
<span>
추가하기
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useState, useEffect } from "react";
import { useState, useEffect, useCallback } from "react";
import { Button, ButtonProvider } from "@/component/common/button";
import { Icon } from "@/component/common/Icon";
import { Spacing } from "@/component/common/Spacing";
Expand All @@ -19,6 +19,8 @@ import AddQuestionView from "./AddQuestionView";
import { useModal } from "@/hooks/useModal";
import { isEqual } from "lodash-es";

const MAX_QUESTION_COUNT = 10;

type QuestionEditSectionProps = {
onClose: () => void;
};
Expand Down Expand Up @@ -95,36 +97,56 @@ export default function QuestionEditSection({ onClose }: QuestionEditSectionProp
const handleDelete = (index: number) => {
const updatedQuestions = questions.filter((_, i) => i !== index);
setEditingQuestions(updatedQuestions);
toast.success("삭제가 완료되었어요!");
};

/**
* 새 질문 추가 핸들러
*/
const handleAddQuestion = () => {
if (questions.length >= 10) return;
if (questions.length >= MAX_QUESTION_COUNT) return;

// 현재 질문들을 백업하고 질문 추가 모드로 전환
setBackupQuestions([...questions]);
const questionsToBackup = [...questions];
setBackupQuestions(questionsToBackup);
setIsAddMode(true);

const cancelCallback = () => {
setEditingQuestions(questionsToBackup);
setIsAddMode(false);
setBackupQuestions([]);
setModalDataState((prev) => ({
...prev,
title: "질문 리스트",
options: {
enableFooter: false,
needsBackButton: true,
backButtonCallback: handleCancel,
},
}));
};

setModalDataState((prev) => ({
...prev,
title: "질문 추가",
onClose: handleAddQuestionCancel,
onClose: cancelCallback,
options: {
enableFooter: false,
needsBackButton: true,
disabledClose: true,
backButtonCallback: handleAddQuestionCancel,
backButtonCallback: cancelCallback,
},
}));
};

/**
* 질문 추가 완료 핸들러 (단일)
* 질문 추가 완료 핸들러
*/
const handleAddQuestionComplete = (content: string) => {
const newQuestions = [...questions, { questionType: "plain_text" as const, questionContent: content }];
const handleAddQuestions = (contents: string[]) => {
const newQuestionObjects = contents.map((content) => ({
questionType: "plain_text" as const,
questionContent: content,
}));
const newQuestions = [...questions, ...newQuestionObjects];
setEditingQuestions(newQuestions);

// 원래 모드로 돌아가고 모달 제목 복원
Expand All @@ -142,32 +164,10 @@ export default function QuestionEditSection({ onClose }: QuestionEditSectionProp
toast.success("질문이 추가되었어요!");
};

/**
* 질문 추가 완료 핸들러 (복수)
*/
const handleAddMultipleQuestions = (contents: string[]) => {
const newQuestionObjects = contents.map((content) => ({
questionType: "plain_text" as const,
questionContent: content,
}));
const newQuestions = [...questions, ...newQuestionObjects];
setEditingQuestions(newQuestions);

// 원래 모드로 돌아가고 모달 제목 복원
setIsAddMode(false);
setModalDataState((prev) => ({
...prev,
title: "질문 리스트",
enableFooter: false,
}));

toast.success(`${contents.length}개의 질문이 추가되었어요!`);
};

/**
* 질문 수정 취소 핸들러 (뒤로가기 버튼)
*/
const handleCancel = () => {
const handleCancel = useCallback(() => {
const hasChanged = !isEqual(originalQuestions, editingQuestions);

if (hasChanged) {
Expand All @@ -186,35 +186,7 @@ export default function QuestionEditSection({ onClose }: QuestionEditSectionProp
} else {
onClose();
}
};

/**
* 질문 추가 취소 핸들러
*/
const handleAddQuestionCancel = () => {
openExitWarningModal({
title: "질문 추가를 취소하시겠어요?",
contents: "추가중인 내용은 모두 사라져요",
onConfirm: () => {
// 백업된 질문들로 복원
setEditingQuestions(backupQuestions);
setIsAddMode(false);
setBackupQuestions([]);
setModalDataState((prev) => ({
...prev,
title: "질문 리스트",
options: {
enableFooter: false,
needsBackButton: true,
backButtonCallback: handleCancel,
},
}));
},
options: {
buttonText: ["취소", "나가기"],
},
});
};
}, [originalQuestions, editingQuestions, onClose, openExitWarningModal]);

/**
* 삭제 모드 진입 핸들러
Expand All @@ -239,8 +211,13 @@ export default function QuestionEditSection({ onClose }: QuestionEditSectionProp
* 삭제 모드 완료 핸들러
*/
const handleDeleteModeComplete = () => {
const hasDeleted = questions.length < backupQuestions.length;
setIsDeleteMode(false);
setBackupQuestions([]);

if (hasDeleted) {
toast.success("삭제가 완료되었어요!");
}
};

// 제출 완료 핸들러
Expand All @@ -263,27 +240,25 @@ export default function QuestionEditSection({ onClose }: QuestionEditSectionProp
onClose();
};

// 모달의 뒤로가기 버튼 콜백을 handleCancel로 설정
// 모달의 뒤로가기 버튼과 닫기 버튼 콜백을 handleCancel로 설정
useEffect(() => {
if (!isAddMode) {
setModalDataState((prev) => ({
...prev,
onClose: handleCancel,
options: {
...prev.options,
disabledClose: true,
backButtonCallback: handleCancel,
},
}));
}
}, [editingQuestions, isAddMode]);
}, [isAddMode, handleCancel]);

return (
<>
{isAddMode ? (
<AddQuestionView
onAddQuestion={handleAddQuestionComplete}
onAddMultipleQuestions={handleAddMultipleQuestions}
maxCount={10 - questions.length}
/>
<AddQuestionView onAddQuestions={handleAddQuestions} maxCount={MAX_QUESTION_COUNT - questions.length} />
) : (
<>
<section
Expand Down Expand Up @@ -317,9 +292,9 @@ export default function QuestionEditSection({ onClose }: QuestionEditSectionProp
{/* ---------- 추가 버튼 ---------- */}
<button
onClick={handleAddQuestion}
disabled={questions.length >= 10}
disabled={questions.length >= MAX_QUESTION_COUNT}
css={css`
background-color: ${questions.length >= 10 ? DESIGN_TOKEN_COLOR.gray200 : DESIGN_TOKEN_COLOR.blue100};
background-color: ${questions.length >= MAX_QUESTION_COUNT ? DESIGN_TOKEN_COLOR.gray200 : DESIGN_TOKEN_COLOR.blue100};
border-radius: 1.2rem;
border: none;
display: flex;
Expand All @@ -328,14 +303,17 @@ export default function QuestionEditSection({ onClose }: QuestionEditSectionProp
height: 4.8rem;
width: 100%;
transition: background-color 0.2s ease;
cursor: ${questions.length >= 10 ? "not-allowed" : "pointer"};

cursor: ${questions.length >= MAX_QUESTION_COUNT ? "not-allowed" : "pointer"};
&:hover {
background-color: ${questions.length >= 10 ? DESIGN_TOKEN_COLOR.gray200 : DESIGN_TOKEN_COLOR.blue200};
background-color: ${questions.length >= MAX_QUESTION_COUNT ? DESIGN_TOKEN_COLOR.gray200 : DESIGN_TOKEN_COLOR.blue200};
}
`}
>
<Icon icon="ic_plus_thin" size={1.8} color={questions.length >= 10 ? DESIGN_TOKEN_COLOR.gray400 : DESIGN_TOKEN_COLOR.blue600} />
<Icon
icon="ic_plus_thin"
size={1.8}
color={questions.length >= MAX_QUESTION_COUNT ? DESIGN_TOKEN_COLOR.gray400 : DESIGN_TOKEN_COLOR.blue600}
/>
</button>
</section>
</section>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,12 @@ export default function TemplateCardManageToggleMenu({
iconSize = 2.0,
iconColor = "gray500",
retrospect,
isLeader,
}: {
iconSize?: number | string;
iconColor?: keyof typeof DESIGN_TOKEN_COLOR;
retrospect: Retrospect;
isLeader: boolean;
}) {
const { isShowMenu, showMenu } = useToggleMenu();
const { mutateAsync: mutateDeleteRetrospect } = useApiDeleteRetrospect();
Expand Down Expand Up @@ -77,6 +79,9 @@ export default function TemplateCardManageToggleMenu({
}
};

// * 회고 카드 관리 메뉴의 경우, 스페이스 리더에게만 노출되도록 합니다.
if (!isLeader) return null;

return (
<div
css={css`
Expand Down
10 changes: 7 additions & 3 deletions lerna.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
{
"npmClient": "pnpm",
"packages": [".", "packages/*", "apps/*"],
"version": "2.0.0"
}
"packages": [
".",
"packages/*",
"apps/*"
],
"version": "2.0.3"
}
Loading