From ba50865e342381db62063211977f3af26ac40c5e Mon Sep 17 00:00:00 2001
From: jsangmin99 <jsangmin99@gmail.com>
Date: Fri, 18 Oct 2024 05:22:59 +0900
Subject: [PATCH 1/2] =?UTF-8?q?[FIX]=20=EC=9E=90=EB=8F=99=EC=99=84?=
 =?UTF-8?q?=EC=84=B1=20=EB=B0=A9=ED=96=A5=ED=82=A4=EC=9D=B4=EB=8F=99=20?=
 =?UTF-8?q?=EB=B0=8F=20=EC=97=94=ED=84=B0=20=EC=B6=94=EA=B0=80=202?=
 =?UTF-8?q?=EA=B8=80=EC=9E=90=20=EC=9D=B4=EC=83=81=20=EC=9E=85=EB=A0=A5?=
 =?UTF-8?q?=EB=A7=8C=20=EA=B0=80=EB=8A=A5=ED=95=98=EB=8F=84=EB=A1=9D=20?=
 =?UTF-8?q?=ED=97=88=EC=9A=A9?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/components/search/SearchComponent.vue | 89 ++++++++++++++++++-----
 1 file changed, 70 insertions(+), 19 deletions(-)

diff --git a/src/components/search/SearchComponent.vue b/src/components/search/SearchComponent.vue
index 5e94e81..b36dd01 100644
--- a/src/components/search/SearchComponent.vue
+++ b/src/components/search/SearchComponent.vue
@@ -3,15 +3,18 @@
     <div class="search-page">
       <!-- 검색 창 -->
       <div class="search-bar">
-        <input ref="searchInput" v-model="keyword" @input="fetchAutocomplete" @keyup.enter="search"
+        <input ref="searchInput" v-model="keyword" @input="fetchAutocomplete" @keydown.down.prevent="moveDown"
+          @keydown.up.prevent="moveUp" @keydown.enter.prevent="handleEnter" @keydown.esc="closeAutocomplete"
           placeholder="검색어를 입력하세요" class="search-input" />
+
         <button @click="search" class="search-button">검색</button>
       </div>
 
       <!-- 자동완성 리스트 -->
       <div v-if="autocompleteSuggestions.length > 0" class="autocomplete-suggestions" ref="autocomplete">
         <ul>
-          <li v-for="suggestion in autocompleteSuggestions" :key="suggestion" @click="selectSuggestion(suggestion)">
+          <li v-for="(suggestion, index) in autocompleteSuggestions" :key="suggestion"
+            @click="selectSuggestion(suggestion)" :class="{ active: index === suggestionIndex }">
             {{ suggestion }}
           </li>
         </ul>
@@ -122,7 +125,6 @@
 
       <div v-if="!loading && results.length === 0" class="no-results-message">No results found.</div>
     </div>
-    <div class="dummy-container"></div>
   </div>
 </template>
 
@@ -143,6 +145,7 @@ export default {
         canvasBlocks: [],
       },
       autocompleteSuggestions: [],
+      suggestionIndex: -1, // 선택된 제안의 인덱스
       loading: false,
       currentPage: 1,
       totalPages: 1,
@@ -170,7 +173,11 @@ export default {
   },
   methods: {
     async search() {
+      if (this.keyword.length < 2) {
+        return; // 검색어가 2글자 미만일 경우 검색하지 않음
+      }
       this.loading = true;
+      this.autocompleteSuggestions = []; // 검색 시 자동완성 리스트 닫기
       this.resetResults();
 
       let url = `${process.env.VUE_APP_API_BASE_URL}/search`;
@@ -216,6 +223,16 @@ export default {
       }
     },
 
+    handleEnter() {
+      // 자동완성 리스트에서 선택된 항목이 있을 때
+      if (this.suggestionIndex !== -1 && this.autocompleteSuggestions[this.suggestionIndex]) {
+        this.selectSuggestion(this.autocompleteSuggestions[this.suggestionIndex]);
+      } else {
+        // 선택된 항목이 없으면 그냥 검색
+        this.search();
+      }
+    },
+
     // 자동완성 데이터 가져오기
     async fetchAutocomplete() {
       if (this.keyword.length < 2) {
@@ -233,12 +250,27 @@ export default {
         });
 
         this.autocompleteSuggestions = response.data?.result || [];
+        this.suggestionIndex = -1; // 자동완성 결과를 가져오면 인덱스를 초기화
+
       } catch (error) {
         console.error('Autocomplete failed:', error);
         this.autocompleteSuggestions = [];
       }
     },
 
+    moveDown() {
+      if (this.autocompleteSuggestions.length > 0 && this.suggestionIndex < this.autocompleteSuggestions.length - 1) {
+        this.suggestionIndex++;
+      }
+    },
+
+    moveUp() {
+      if (this.autocompleteSuggestions.length > 0 && this.suggestionIndex > 0) {
+        this.suggestionIndex--;
+      }
+    },
+
+
     handleClickOutside(event) {
       const autocomplete = this.$refs.autocomplete;
       const searchInput = this.$refs.searchInput;
@@ -253,9 +285,16 @@ export default {
     },
 
     selectSuggestion(suggestion) {
-      this.keyword = suggestion;
+      if (suggestion) {
+        this.keyword = suggestion;
+        this.closeAutocomplete();
+        this.search();
+      }
+    },
+
+    closeAutocomplete() {
       this.autocompleteSuggestions = [];
-      this.search();
+      this.suggestionIndex = -1;
     },
 
     setTab(tab) {
@@ -335,15 +374,19 @@ export default {
   background-color: #f1f1f1;
 }
 
+.autocomplete-suggestions li.active {
+  background-color: #e0e0e0;
+}
+
 .search-page-container {
   display: flex;
   justify-content: space-between;
 }
 
-.dummy-container {
+/* .dummy-container {
   width: 15%;
-  /* 오른쪽 여백을 15%로 설정 */
-}
+ } 
+*/
 
 .search-page {
   flex: 1;
@@ -361,41 +404,49 @@ export default {
 .search-bar {
   display: flex;
   justify-content: flex-start;
-  /* 왼쪽 정렬 */
+  align-items: center;
   margin-bottom: 20px;
   position: relative;
-  /* 부모 요소를 기준으로 자동완성 리스트 위치를 설정 */
 }
 
 .search-input {
   width: 80%;
-  padding: 12px;
-  border: 2px solid #ccc;
-  border-radius: 4px;
+  padding: 12px 15px;
+  border: 2px solid #ddd;
+  border-radius: 50px;
   font-size: 16px;
+  transition: all 0.3s ease;
+  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
 }
 
 .search-input:focus {
   outline: none;
-  border-color: #555;
-  box-shadow: 0 0 5px rgba(0, 0, 0, 0.1);
+  border-color: #3a8bcd;
+  box-shadow: 0 4px 8px rgba(58, 139, 205, 0.3);
 }
 
 .search-button {
   padding: 12px 20px;
-  margin-left: 10px;
-  /* 버튼을 왼쪽으로 살짝 이동 */
+  margin-left: -50px;
+  /* 버튼을 검색 바 안으로 겹치게 하기 위해 왼쪽 마진을 설정 */
   background-color: #3a8bcd;
   color: white;
   border: none;
-  border-radius: 4px;
+  border-radius: 50px;
   cursor: pointer;
   font-size: 16px;
-  transition: background-color 0.3s ease;
+  transition: background-color 0.3s ease, transform 0.2s ease;
+  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
 }
 
 .search-button:hover {
   background-color: #337ab7;
+  transform: scale(1.05);
+}
+
+.search-button:active {
+  background-color: #2a5f8f;
+  transform: scale(0.98);
 }
 
 .result-card {

From 5b7885055d0bfba317137c801c451ecc70cf3a59 Mon Sep 17 00:00:00 2001
From: jeonsangmin <jsangmin99@gmail.com>
Date: Fri, 18 Oct 2024 16:21:10 +0900
Subject: [PATCH 2/2] =?UTF-8?q?[fix]=20=EA=B2=80=EC=83=89=EC=97=90?=
 =?UTF-8?q?=EC=84=9C=20=EC=9D=B4=EB=8F=99=EC=8B=9C=20=EA=B8=B0=EB=B3=B8=20?=
 =?UTF-8?q?null=20=EA=B0=92=20=EB=8B=B4=EA=B8=B0=EB=8A=94=20=EB=AC=B8?=
 =?UTF-8?q?=EC=A0=9C=20=ED=95=B4=EA=B2=B0?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/components/search/SearchComponent.vue | 55 ++++++++++++++---------
 1 file changed, 34 insertions(+), 21 deletions(-)

diff --git a/src/components/search/SearchComponent.vue b/src/components/search/SearchComponent.vue
index 50ee280..5caa13f 100644
--- a/src/components/search/SearchComponent.vue
+++ b/src/components/search/SearchComponent.vue
@@ -74,7 +74,8 @@
 
           <div v-if="totalThreads > 0" class="category-section">
             <h3>쓰레드 검색 결과 ({{ totalThreads }})</h3>
-            <div v-for="(result, index) in results.threads" :key="index" class="result-card" @click="moveToThread(result.channelId, result.threadId)">
+            <div v-for="(result, index) in results.threads" :key="index" class="result-card"
+              @click="moveToThread(result.channelId, result.threadId)">
               <h3>{{ result.content || '내용 없음' }}</h3>
               <p class="metadata">Posted by: {{ result.memberName }} | {{ result.createdTime }}</p>
             </div>
@@ -171,14 +172,19 @@ export default {
   beforeUnmount() {
     document.removeEventListener('click', this.handleClickOutside);
   },
+  watch: {
+    activeTab() {
+      this.search(); // 자동으로 검색을 트리거
+    }
+  },
   methods: {
+
     async search() {
-      if (this.keyword.length < 2) {
-        return; // 검색어가 2글자 미만일 경우 검색하지 않음
+      if (!this.keyword || this.keyword.length < 2) {
+        return; // 검색어가 없거나 2글자 미만일 경우 검색하지 않음
       }
       this.loading = true;
       this.autocompleteSuggestions = []; // 검색 시 자동완성 리스트 닫기
-      this.resetResults();
 
       let url = `${process.env.VUE_APP_API_BASE_URL}/search`;
       if (this.activeTab !== 'ALL') {
@@ -199,7 +205,13 @@ export default {
         if (response.data && response.data.result) {
           // 전체 검색일 경우 결과를 카테고리별로 저장
           if (this.activeTab === 'ALL') {
-            this.results = response.data.result;
+            this.results = {
+              workspaceMembers: response.data.result.workspaceMembers || [],
+              files: response.data.result.files || [],
+              channels: response.data.result.channels || [],
+              threads: response.data.result.threads || [],
+              canvasBlocks: response.data.result.canvasBlocks || [],
+            };
             this.totalMembers = response.data.result.totalMembers || 0;
             this.totalFiles = response.data.result.totalFiles || 0;
             this.totalChannels = response.data.result.totalChannels || 0;
@@ -215,6 +227,8 @@ export default {
             this.results = response.data.result.results || [];
             this.totalPages = Math.ceil(response.data.result.total / this.pageSize);
           }
+        } else {
+          this.resetResults(); // 검색 결과가 없을 경우에도 결과 초기화
         }
       } catch (error) {
         console.error('Search failed:', error);
@@ -270,6 +284,13 @@ export default {
       }
     },
 
+    moveToThread(channelId, threadId) {
+      this.$router.push({
+        path: `/channel/${channelId}/thread/view`,
+        query: { threadId }
+      });
+    },
+
 
     handleClickOutside(event) {
       const autocomplete = this.$refs.autocomplete;
@@ -300,7 +321,6 @@ export default {
     setTab(tab) {
       this.activeTab = tab;
       this.currentPage = 1;
-      this.search();
     },
 
     nextPage() {
@@ -318,28 +338,21 @@ export default {
     },
 
     resetResults() {
-      this.results = {
-        workspaceMembers: [],
-        files: [],
-        channels: [],
-        threads: [],
-        canvasBlocks: [],
-      };
-    },
-    moveToThread(channelId, threadId){
-      this.$router.push({
-        path: `/channel/${channelId}/thread/view`,
-        query: { threadId }
-      });
+      this.results = {}; // results를 빈 객체로 초기화
+      this.totalMembers = 0;
+      this.totalFiles = 0;
+      this.totalChannels = 0;
+      this.totalThreads = 0;
+      this.totalCanvasBlocks = 0;
+      this.totalAll = 0;
     }
   }
 };
+
 </script>
 
 <style scoped>
 /* 스크롤 추가 */
-
-
 .result-container {
   min-height: 300px;
   /* 최소 높이를 300px로 설정 */