From ed992e1c2d3aa9a04ec921947f361d8efc8affaf Mon Sep 17 00:00:00 2001
From: Dennis Postma <dennis@directonline.io>
Date: Tue, 18 Feb 2025 18:27:35 +0100
Subject: [PATCH] Updated characters.vue

---
 src/components/screens/Characters.vue | 68 +++++++++++++++++----------
 1 file changed, 44 insertions(+), 24 deletions(-)

diff --git a/src/components/screens/Characters.vue b/src/components/screens/Characters.vue
index 68869c5..2994d1c 100644
--- a/src/components/screens/Characters.vue
+++ b/src/components/screens/Characters.vue
@@ -22,7 +22,7 @@
               </div>
               <div class="character relative rounded default-border w-12 h-12 bg-[url('/assets/ui-texture.png')]" :class="{ active: characters.length == 0 }" v-if="characters.length < 4">
                 <button class="p-0 h-full w-full flex flex-col justify-between focus-visible:outline-offset-0 btn-sound" @click="isCreateNewCharacterModalOpen = true">
-                  <img class="w-6 h-6 object-contain center-element btn-sound" draggable="false" src="/assets/icons/plus-icon.svg" />
+                  <img class="w-6 h-6 object-contain center-element btn-sound" draggable="false" src="/assets/icons/plus-icon.svg" alt="Add character" />
                 </button>
               </div>
             </div>
@@ -40,22 +40,27 @@
                     </button>
                   </div>
                 </div>
-                <!-- TODO: update gender on (selected) character -->
-                <!--                <div class="flex justify-between w-[190px]">-->
-                <!--                  <button class="btn-empty flex gap-2" :class="{ selected: characters.find((c) => c.id == selectedCharacterId)?.characterType?.gender === 'MALE' }">-->
-                <!--                    <img src="/assets/icons/male-icon.svg" class="w-4 h-4 m-auto" alt="Male symbol" />-->
-                <!--                    <span class="text-white">Male</span>-->
-                <!--                  </button>-->
-                <!--                  <button class="btn-empty flex gap-2" :class="{ selected: characters.find((c) => c.id == selectedCharacterId)?.characterType?.gender === 'FEMALE' }">-->
-                <!--                    <img src="/assets/icons/male-icon.svg" class="w-4 h-4 m-auto" alt="Male symbol" />-->
-                <!--                    <span class="text-white">Female</span>-->
-                <!--                  </button>-->
-                <!--                </div>-->
+                <div class="flex justify-between w-[190px]">
+                  <button class="btn-empty flex gap-2 selected">
+                    <img src="/assets/icons/male-icon.svg" class="w-4 h-4 m-auto" alt="Male symbol" />
+                    <span class="text-white">Male</span>
+                  </button>
+                  <button class="btn-empty flex gap-2">
+                    <img src="/assets/icons/male-icon.svg" class="w-4 h-4 m-auto" alt="Male symbol" />
+                    <span class="text-white">Female</span>
+                  </button>
+                </div>
               </div>
             </div>
           </div>
           <div class="flex-1 lg:w-2/3 max-lg:min-h-[212px] h-full bg-[url('/assets/ui-texture.png')] bg-no-repeat bg-cover bg-center max-lg:rounded-bl-md rounded-r-md">
             <div class="py-6 px-8 h-[calc(100%_-_48px)] flex flex-col items-center gap-10" v-if="selectedCharacterId">
+              <div class="flex flex-col gap-3 w-full">
+                <span class="text-sm">Hair color</span>
+                <div class="flex gap-2 flex-wrap">
+                  <input type="radio" name="hair-color" v-for="color in uniqueHairColors" class="w-6 h-6 m-0 rounded-sm hover:cursor-pointer checked:outline checked:outline-1 checked:outline-white" :style="getHairColorStyle(color)" @click="handleHairColorChange(color)" />
+                </div>
+              </div>
               <div class="flex flex-col gap-3 w-full">
                 <span class="text-sm">Hairstyle</span>
                 <div class="flex gap-2 flex-wrap max-h-20 overflow-y-auto scrollbar">
@@ -65,9 +70,8 @@
                     <img src="/assets/icons/x-button-gray.svg" class="w-4 h-4" alt="Empty button" />
                     <input type="radio" name="hair" :value="null" v-model="selectedHairId" class="h-full w-full absolute left-0 top-0 m-0 z-10 hover:cursor-pointer focus-visible:outline-offset-0 focus-visible:outline-white" />
                   </div>
-                  <!-- TODO #255: make radio button so we can set a value, do the same with swatches -->
                   <div
-                    v-for="hair in characterHairs"
+                    v-for="hair in filteredHairs"
                     class="relative flex justify-center items-center bg-gray default-border w-[18px] h-[18px] p-2 rounded-sm hover:bg-gray-500 hover:border-gray-400 focus-visible:outline-none focus-visible:border-gray-300 focus-visible:bg-gray-500 has-[:checked]:bg-cyan has-[:checked]:border-transparent"
                   >
                     <img class="h-4 object-contain" :src="config.server_endpoint + '/textures/sprites/' + hair.sprite + '/front.png'" alt="Hair sprite" />
@@ -75,13 +79,6 @@
                   </div>
                 </div>
               </div>
-              <div class="flex flex-col gap-3 w-full">
-                <span class="text-sm">Hair color</span>
-                <div class="flex gap-2 flex-wrap">
-                  <!-- TODO: replace with hair colors -->
-                  <input type="radio" name="hair-color" v-for="n in 10" class="bg-red w-6 h-6 m-0 rounded-sm hover:cursor-pointer checked:outline checked:outline-1 checked:outline-white" />
-                </div>
-              </div>
             </div>
           </div>
         </div>
@@ -89,7 +86,6 @@
       <div v-else>
         <img class="w-20 invert-80" src="/assets/icons/loading-icon1.svg" alt="Loading" />
       </div>
-
       <div class="w-2/3 button-wrapper flex self-center justify-center lg:justify-end gap-4 max-w-[860px]" v-if="!isLoading">
         <button class="btn-empty min-w-48" @click.stop="gameStore.disconnectSocket()">Back</button>
         <button class="btn-cyan min-w-48 disabled:bg-cyan-800 disabled:cursor-not-allowed" :disabled="!selectedCharacterId" @click="loginWithCharacter()">Play now</button>
@@ -129,7 +125,7 @@ import { useSoundComposable } from '@/composables/useSoundComposable'
 import { socketManager } from '@/managers/SocketManager'
 import { CharacterHairStorage } from '@/storage/storages'
 import { useGameStore } from '@/stores/gameStore'
-import { onBeforeUnmount, onMounted, ref, watch } from 'vue'
+import { computed, onBeforeUnmount, onMounted, ref, watch } from 'vue'
 
 const { playSound } = useSoundComposable()
 const gameStore = useGameStore()
@@ -141,6 +137,31 @@ const newCharacterName = ref<string>('')
 const characterHairs = ref<CharacterHair[]>([])
 const selectedHairId = ref<string | null>(null)
 
+const selectedHairColor = ref<string | null>(null)
+const uniqueHairColors = computed(() => {
+  return [...new Set(characterHairs.value.map((hair) => hair.color))]
+})
+
+const filteredHairs = computed(() => {
+  if (!selectedHairColor.value) return characterHairs.value
+  return characterHairs.value.filter((hair) => hair.color === selectedHairColor.value)
+})
+
+function handleHairColorChange(color: string | null) {
+  selectedHairColor.value = color
+  // Reset hair selection if the current selection doesn't exist in filtered results
+  if (!filteredHairs.value.find((hair) => hair.id === selectedHairId.value)) {
+    selectedHairId.value = null
+  }
+}
+
+function getHairColorStyle(color: string | null) {
+  return {
+    backgroundColor: color,
+    border: selectedHairColor.value === color ? '1px solid white' : '1px solid rgba(255, 255, 255, 0.2)'
+  }
+}
+
 // Fetch characters
 setTimeout(() => {
   socketManager.emit(SocketEvent.CHARACTER_LIST)
@@ -151,7 +172,6 @@ socketManager.on(SocketEvent.CHARACTER_LIST, (data: any) => {
   isLoading.value = false
 })
 
-// Select character logics
 function loginWithCharacter() {
   if (!selectedCharacterId.value) return