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
135 changes: 66 additions & 69 deletions src/components/Team/addTeamButton.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,81 +2,78 @@ import React, { useState } from "react";
import { Plus } from "lucide-react";
import { Button } from "@/components/ui/button";
import {
Dialog,
DialogTrigger,
DialogContent,
DialogHeader,
DialogTitle,
DialogFooter,
DialogClose,
Dialog,
DialogTrigger,
DialogContent,
DialogHeader,
DialogTitle,
DialogFooter,
DialogClose,
} from "@/components/ui/dialog";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Textarea } from "@/components/ui/textarea";

export default function AddTeamButton({ onCreate, isCreating }) {
const [teamName, setTeamName] = useState("");
const [teamDesc, setTeamDesc] = useState("");

const [teamName, setTeamName] = useState("");
const [teamDesc, setTeamDesc] = useState("");

const handleSubmit = () => {
if (!teamName.trim()) return;
onCreate({
teamName: teamName,
teamDescription: teamDesc,
});
setTeamName("");
setTeamDesc("");
};

const handleSubmit = () => {
if (!teamName.trim()) return;
onCreate({
teamName:teamName,
teamDescription:teamDesc
});
setTeamName("");
setTeamDesc("");
};


return (
<Dialog>
<DialogTrigger asChild>
<Button variant="default">
<Plus size={16} />
팀 생성
</Button>
</DialogTrigger>
<DialogContent className="sm:max-w-[400px]">
<DialogHeader>
<DialogTitle>팀 생성</DialogTitle>
</DialogHeader>
<div className="grid gap-4 py-4">
<div className="grid gap-2">
<Label htmlFor="team-name">팀 이름</Label>
<Input
id="team-name"
value={teamName}
onChange={(e) => setTeamName(e.target.value)}
placeholder="예: 타피오카"
/>
</div>
<div className="grid gap-2">
<Label htmlFor="team-desc">설명 (선택)</Label>
<Textarea
id="team-desc"
value={teamDesc}
onChange={(e) => setTeamDesc(e.target.value)}
placeholder="팀에 대한 간단한 소개"
/>
</div>
</div>
<DialogFooter>
<DialogClose asChild>
<Button variant="outline">취소</Button>
</DialogClose>
<DialogClose asChild>
<Button
onClick={handleSubmit}
disabled={!teamName.trim() || isCreating}
>
{isCreating ? '생성 중...' : '생성'}
</Button>
</DialogClose>
</DialogFooter>
</DialogContent>
</Dialog>
);
return (
<Dialog>
<DialogTrigger asChild>
<Button variant="default">
<Plus size={16} />팀 생성
</Button>
</DialogTrigger>
<DialogContent className="sm:max-w-[400px]">
<DialogHeader>
<DialogTitle>팀 생성</DialogTitle>
</DialogHeader>
<div className="grid gap-4 py-4">
<div className="grid gap-2">
<Label htmlFor="team-name">팀 이름</Label>
<Input
id="team-name"
value={teamName}
onChange={(e) => setTeamName(e.target.value)}
placeholder="예: 타피오카"
autoComplete="off"
/>
</div>
<div className="grid gap-2">
<Label htmlFor="team-desc">설명 (선택)</Label>
<Textarea
id="team-desc"
value={teamDesc}
onChange={(e) => setTeamDesc(e.target.value)}
placeholder="팀에 대한 간단한 소개"
/>
</div>
</div>
<DialogFooter>
<DialogClose asChild>
<Button variant="outline">취소</Button>
</DialogClose>
<DialogClose asChild>
<Button
onClick={handleSubmit}
disabled={!teamName.trim() || isCreating}
>
{isCreating ? "생성 중..." : "생성"}
</Button>
</DialogClose>
</DialogFooter>
</DialogContent>
</Dialog>
);
}
52 changes: 42 additions & 10 deletions src/pages/Dashboard.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -84,24 +84,36 @@ const Dashboard = ({ setActiveTab, setSpecData, setTrafficData }) => {
// },
// },
// });

const [messages, setMessages] = useState([
{ role: "bot", text: "안녕하세요! 무엇을 도와드릴까요?" },
]);
const [input, setInput] = useState("");
const messagesEndRef = useRef(null);
const params = useParams();
const teamCode = params.teamCode;
const inputRef = useRef(null);
// ✅ 팀코드 기반 저장된 메시지 불러오기
const STORAGE_KEY = `chat_messages_${teamCode}`;

const [messages, setMessages] = useState(() => {
const saved = sessionStorage.getItem(STORAGE_KEY);
return saved
? JSON.parse(saved)
: [{ role: "bot", text: "안녕하세요! 무엇을 도와드릴까요?" }];
});

// 2. useSendMessage 훅을 호출하고, mutate 함수와 로딩 상태(isPending)를 가져옵니다.
const { mutate: postPrompt, isPending } = usePostPrompt();

const scrollToBottom = () => {
messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
};
// const scrollToBottom = () => {
// messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
// };

// ✅ 메시지 저장
useEffect(() => {
scrollToBottom();
sessionStorage.setItem(STORAGE_KEY, JSON.stringify(messages));
}, [messages, STORAGE_KEY]);

// ✅ 스크롤 항상 아래로
useEffect(() => {
messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
}, [messages]);

const handleSend = (e) => {
Expand Down Expand Up @@ -149,6 +161,9 @@ const Dashboard = ({ setActiveTab, setSpecData, setTrafficData }) => {
},
}
);
if (inputRef.current) {
inputRef.current.style.height = "48px";
}
};

return (
Expand Down Expand Up @@ -181,14 +196,31 @@ const Dashboard = ({ setActiveTab, setSpecData, setTrafficData }) => {
</div>

<form className="chat-input" onSubmit={handleSend}>
<input
type="text"
<textarea
value={input}
ref={inputRef}
onChange={(e) => setInput(e.target.value)}
placeholder={
isPending ? "응답을 기다리는 중..." : "메시지를 입력하세요"
}
disabled={isPending}
rows={1}
style={{ overflow: "hidden", resize: "none" }}
onInput={(e) => {
const textarea = e.target;
textarea.style.height = "auto";

const maxHeight = 4 * 24;
const newHeight = Math.min(textarea.scrollHeight, maxHeight);

textarea.style.height = `${newHeight}px`;
}}
onKeyDown={(e) => {
if (e.key === "Enter" && !e.shiftKey) {
e.preventDefault();
handleSend(e);
}
}}
/>
<button type="submit" disabled={!input.trim() || isPending}>
<img
Expand Down
2 changes: 2 additions & 0 deletions src/pages/Home.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ const Home = () => {

await checkServerStatus(url);
alert("서버가 성공적으로 등록되었습니다.");

triggerServerRefetch();
} catch (error) {
const errorCode = error?.response?.data?.code || error?.code || null;
const errorMessage =
Expand Down
15 changes: 10 additions & 5 deletions src/styles/css/Dashboard.css
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
background-color: #f1f1f1;
padding: 8px 12px;
border-radius: 16px;
width: -moz-fit-content;
width: fit-content;
max-width: 100%;
}
Expand Down Expand Up @@ -81,22 +82,26 @@
gap: 10px;
}

.chat-input input {
flex: 1;
.chat-input textarea {
width: 100%;
padding: 12px;
border: 1px solid #D9D9D9;
border-radius: 16px;
font-size: 14px;
line-height: 20px;
font-weight: 400;
letter-spacing: -2.5%;
color: #D9D9D9;
color: #111;
line-height: 24px;
resize: none;
overflow: hidden;
line-height: 24px;
min-height: 24px;
max-height: 96px;
}
.chat-input input:focus {
.chat-input textarea:focus {
border-color: #111;
outline: none;
color: #111;
}

.chat-input button {
Expand Down
18 changes: 11 additions & 7 deletions src/styles/scss/pages/Dashboard.scss
Original file line number Diff line number Diff line change
Expand Up @@ -80,19 +80,23 @@
gap: 10px;
}

.chat-input input {
flex: 1;
.chat-input textarea {
width: 100%;
padding: 12px;
border: 1px solid #D9D9D9;
border-radius: 16px;
@include font-style("body2");
color: #D9D9D9;
@include font-style("body2");
color: #111;

line-height: 24px;
resize: none;
overflow: hidden;
line-height: 24px;
min-height: 24px; // 1줄 기준
max-height: 96px; // 4줄 기준

&:focus {
border-color: #111;
outline: none;
color: #111;
outline: none;
}
}

Expand Down