{"file":"index-DTCTQrHx.js","mappings":"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","names":["_ssrRenderAttr","_unref","_ssrRenderStyle","_push","_parent","index","_createVNode","_createBlock","_openBlock","_Fragment","_renderList"],"sources":["../../../../composables/useProfile.js","../../../../pages/mypage/index.vue"],"sourcesContent":["import axios from 'axios';\n\nexport const useProfile = () => {\n  const updateProfile = async (profileData) => {\n    try {\n      const response = await axios.post('/api/user.php?action=update', profileData);\n      return response.data;\n    } catch (error) {\n      console.error('프로필 업데이트 실패:', error);\n      return {\n        success: false,\n        message: error.response?.data?.message || '프로필 업데이트에 실패했습니다.'\n      };\n    }\n  };\n\n  return {\n    updateProfile\n  };\n};\n","<script setup>\nimport { Cropper } from \"vue-advanced-cropper\";\nimport \"vue-advanced-cropper/dist/style.css\";\nimport { ref, computed, nextTick, onMounted } from \"vue\";\nimport {\n  useFormField,\n  usePasswordField,\n  usePasswordConfirmField,\n  useFormValidation,\n  useFormChanges,\n} from \"~/composables/useForm\";\nimport { useAuth } from \"~/composables/useAuth\";\nimport { useProfile } from \"~/composables/useProfile\";\n\ndefinePageMeta({\n  layout: \"mypage\",\n});\n\n// ===== Auth & Profile =====\nconst { currentUser } = useAuth();\nconst { updateProfile } = useProfile();\n\n// ===== 사용자 정보 =====\nconst userNickname = computed(() => currentUser.value?.nickname || \"\");\nconst userId = computed(() => currentUser.value?.id || \"\");\nconst userAvatar = computed(() => currentUser.value?.avatar || \"/images/default_avatar_01.png\");\n\n// ===== 초기값 =====\nconst initialValues = ref({\n  nickname: \"\",\n  password: \"\",\n  avatar: \"/images/default_avatar_01.png\",\n});\n\n// ===== 폼 필드 =====\n// 닉네임 필드\nconst nickname = useFormField(\"\", {\n  required: true,\n  errorMessages: {\n    empty: \"닉네임을 입력해주세요.\",\n  },\n});\n\n// Load user data on mount\nonMounted(() => {\n  if (currentUser.value) {\n    nickname.value.value = currentUser.value.nickname || \"\";\n    currentAvatar.value = currentUser.value.avatar || \"/images/default_avatar_01.png\";\n    initialValues.value.nickname = currentUser.value.nickname || \"\";\n    initialValues.value.avatar = currentUser.value.avatar || \"/images/default_avatar_01.png\";\n  }\n});\n\n// 비밀번호 필드\nconst password = usePasswordField(\"\", {\n  required: false,\n  errorMessages: {\n    empty: \"비밀번호를 입력해주세요.\",\n    invalid: \"비밀번호는 영문숫자 포함 8자 이상이어야 합니다.\",\n  },\n});\n\n// 비밀번호 확인 필드\nconst passwordConfirm = usePasswordConfirmField(\"\", password, {\n  errorMessages: {\n    empty: \"비밀번호를 입력해주세요.\",\n    mismatch: \"비밀번호가 일치하지 않습니다.\",\n  },\n});\n\n// ===== 아바타 관련 상태 =====\n// 프로필 편집 모달 ref\nconst editProfileModal = ref(null);\n// 현재 설정된 아바타\nconst currentAvatar = ref(\"/images/default_avatar_01.png\");\n// 모달에서 선택한 아바타 (미리보기용)\nconst selectedAvatar = ref(\"/images/default_avatar_01.png\");\n// 업로드한 이미지 파일\nconst uploadedImageFile = ref(null);\n// 크롭할 이미지 소스\nconst cropImageSrc = ref(null);\n// 크롭퍼 컴포넌트 ref\nconst cropperRef = ref(null);\n// 기본 아바타 목록\nconst defaultAvatars = [\n  \"/images/default_avatar_01.png\",\n  \"/images/default_avatar_02.png\",\n  \"/images/default_avatar_03.png\",\n  \"/images/default_avatar_04.png\",\n  \"/images/default_avatar_05.png\",\n  \"/images/default_avatar_06.png\",\n];\n\n// ===== 폼 관리 =====\nconst form = useFormValidation(\n  {\n    nickname,\n    password,\n    passwordConfirm,\n  },\n  {\n    onSubmit: async (values) => {\n      const profileData = {\n        nickname: values.nickname,\n        avatar: currentAvatar.value\n      };\n\n      // 비밀번호가 입력된 경우만 추가\n      if (values.password) {\n        profileData.password = values.password;\n      }\n\n      const result = await updateProfile(profileData);\n\n      if (!result.success) {\n        alert(result.message || '프로필 업데이트에 실패했습니다.');\n        return false;\n      }\n\n      // 성공 시 사용자 정보 새로고침 (모든 컴포넌트에 즉시 반영)\n      await refreshUser();\n\n      // 초기값 업데이트\n      initialValues.value.nickname = values.nickname;\n      initialValues.value.password = \"\";\n      initialValues.value.avatar = currentAvatar.value;\n\n      // 비밀번호 필드 초기화\n      password.value.value = \"\";\n      passwordConfirm.value.value = \"\";\n\n      alert('프로필이 업데이트되었습니다.');\n      return true;\n    },\n  }\n);\n\n// 변경 사항 추적\nconst changes = useFormChanges(\n  { nickname, password },\n  computed(() => ({\n    nickname: initialValues.value.nickname,\n    password: initialValues.value.password,\n  }))\n);\n\n// 아바타 변경 여부\nconst hasAvatarChanged = computed(() => {\n  return currentAvatar.value !== initialValues.value.avatar;\n});\n\n// 저장 버튼 비활성화 여부\nconst isSaveDisabled = computed(() => {\n  // 에러가 있으면 비활성화\n  if (\n    nickname.isError.value ||\n    password.isError.value ||\n    passwordConfirm.isError.value\n  ) {\n    return true;\n  }\n\n  // 닉네임이 비어있으면 비활성화\n  if (!nickname.value.value.trim()) {\n    return true;\n  }\n\n  // 비밀번호가 입력되었는데 유효하지 않으면 비활성화\n  if (password.value.value && !password.validate()) {\n    return true;\n  }\n\n  // 비밀번호 확인이 일치하지 않으면 비활성화\n  if (password.value.value && !passwordConfirm.validateMatch()) {\n    return true;\n  }\n\n  // 변경사항이 없으면 비활성화\n  if (!changes.hasAnyChanges.value && !hasAvatarChanged.value) {\n    return true;\n  }\n\n  return false;\n});\n\n// ===== 아바타 관련 함수 =====\n// 선택된 아바타 미리보기 이미지\nconst previewAvatar = computed(() => {\n  const isDefaultAvatar = defaultAvatars.includes(selectedAvatar.value);\n  if (isDefaultAvatar) {\n    const match = selectedAvatar.value.match(/default_avatar_(\\d+)\\.png/);\n    if (match) {\n      const number = match[1];\n      return `/images/default_avatar_preview_${number}.png`;\n    }\n  }\n  return selectedAvatar.value;\n});\n\n// 아바타 선택 핸들러\nconst selectAvatar = (avatarPath) => {\n  selectedAvatar.value = avatarPath;\n  cropImageSrc.value = null;\n  uploadedImageFile.value = null;\n};\n\n// 파일 선택 핸들러\nconst handleFileSelect = (event) => {\n  const file = event.target.files?.[0];\n  if (!file) return;\n\n  if (!file.type.startsWith(\"image/\")) {\n    alert(\"이미지 파일만 선택 가능합니다.\");\n    return;\n  }\n\n  const reader = new FileReader();\n  reader.onload = (e) => {\n    cropImageSrc.value = e.target.result;\n    uploadedImageFile.value = file;\n    selectedAvatar.value = null;\n  };\n  reader.readAsDataURL(file);\n\n  event.target.value = \"\";\n};\n\n// 크롭 결과 변경 핸들러\nconst handleCropChange = ({ canvas }) => {\n  if (canvas) {\n    const croppedCanvas = document.createElement(\"canvas\");\n    croppedCanvas.width = 256;\n    croppedCanvas.height = 256;\n    const ctx = croppedCanvas.getContext(\"2d\");\n    ctx.drawImage(canvas, 0, 0, 256, 256);\n    selectedAvatar.value = croppedCanvas.toDataURL(\"image/png\");\n  }\n};\n\n// 크롭퍼 준비 완료 핸들러\nconst handleCropReady = () => {\n  if (cropperRef.value) {\n    nextTick(() => {\n      const result = cropperRef.value?.getResult();\n      if (result && result.canvas) {\n        const croppedCanvas = document.createElement(\"canvas\");\n        croppedCanvas.width = 256;\n        croppedCanvas.height = 256;\n        const ctx = croppedCanvas.getContext(\"2d\");\n        ctx.drawImage(result.canvas, 0, 0, 256, 256);\n        selectedAvatar.value = croppedCanvas.toDataURL(\"image/png\");\n      }\n    });\n  }\n};\n\n// 프로필 편집 모달 열기\nconst editProfile = () => {\n  selectedAvatar.value = currentAvatar.value;\n  uploadedImageFile.value = null;\n  cropImageSrc.value = null;\n  editProfileModal.value?.open();\n};\n\n// 아바타 저장 핸들러\nconst saveAvatar = (close) => {\n  currentAvatar.value = selectedAvatar.value;\n  close();\n};\n\n// ===== 폼 제출 =====\nconst handleSave = async () => {\n  await form.handleSubmit();\n};\n</script>\n\n<template>\n  <div class=\"mypage__group\">\n    <div class=\"mypage__title-sub\">\n      <h3>프로필 설정</h3>\n    </div>\n    <div class=\"mypage__content\">\n      <!-- ===== 프로필 이미지 영역 ===== -->\n      <div class=\"mypage__thumb\">\n        <div class=\"mypage__thumb-profile\">\n          <img :src=\"currentAvatar\" alt=\"\" />\n          <button\n            type=\"button\"\n            class=\"mypage__thumb-edit ico-before ico-photo\"\n            @click=\"editProfile\"\n          >\n            <span class=\"blind\">프로필 변경</span>\n          </button>\n        </div>\n        <div class=\"mypage__thumb-name\">\n          <strong class=\"mypage__thumb-name-title\">{{ userNickname }}</strong>\n          <p class=\"mypage__thumb-name-id\">\n            <span>ID:</span>\n            <span>{{ userId }}</span>\n          </p>\n        </div>\n      </div>\n\n      <!-- ===== 입력 폼 영역 ===== -->\n      <div class=\"mypage__info\">\n        <!-- 닉네임 -->\n        <div class=\"mypage__info-item\">\n          <label for=\"nickname\" class=\"mypage__info-label\">닉네임</label>\n          <input\n            ref=\"nickname.inputRef\"\n            type=\"text\"\n            class=\"input-text\"\n            :class=\"{\n              'input-error': nickname.isError.value,\n              'input-shake': nickname.shakeKey.value > 0,\n            }\"\n            :key=\"`nickname-${nickname.shakeKey.value}`\"\n            id=\"nickname\"\n            placeholder=\"닉네임을 입력해주세요.\"\n            v-model=\"nickname.value.value\"\n            @blur=\"nickname.handleBlur\"\n          />\n          <Transition name=\"error\">\n            <span v-if=\"nickname.isError.value\" class=\"mypage__info-error\">\n              {{ nickname.errorMessage.value }}\n            </span>\n          </Transition>\n        </div>\n        <!-- ID (읽기 전용) -->\n        <div class=\"mypage__info-item\">\n          <label for=\"userid\" class=\"mypage__info-label\">ID</label>\n          <input\n            type=\"text\"\n            class=\"input-text\"\n            id=\"userid\"\n            :value=\"userId\"\n            readonly\n          />\n        </div>\n        <!-- 비밀번호 변경 -->\n        <div class=\"mypage__info-item\">\n          <label for=\"password\" class=\"mypage__info-label\">비밀번호 변경</label>\n          <div class=\"mypage__info-eye-wrap\">\n            <input\n              ref=\"password.inputRef\"\n              :type=\"password.showPassword.value ? 'text' : 'password'\"\n              class=\"input-text input-pw\"\n              :class=\"{\n                'input-error': password.isError.value,\n                'input-shake': password.shakeKey.value > 0,\n              }\"\n              :key=\"`password-${password.shakeKey.value}`\"\n              id=\"password\"\n              placeholder=\"비밀번호를 입력해주세요.\"\n              v-model=\"password.value.value\"\n              @focus=\"password.handleFocus\"\n              @blur=\"password.handleBlur\"\n            />\n            <button\n              v-show=\"password.value.value.length > 0\"\n              type=\"button\"\n              class=\"mypage__info-eye\"\n              :class=\"\n                password.showPassword.value ? 'ico-eye-active' : 'ico-eye'\n              \"\n              @click=\"password.togglePassword\"\n            >\n              <span class=\"blind\">비밀번호 보기</span>\n            </button>\n          </div>\n          <Transition name=\"error\">\n            <span v-if=\"password.isError.value\" class=\"mypage__info-error\">\n              {{ password.errorMessage.value }}\n            </span>\n          </Transition>\n          <!-- 비밀번호 확인 -->\n          <div class=\"mypage__info-eye-wrap\">\n            <input\n              ref=\"passwordConfirm.inputRef\"\n              :type=\"passwordConfirm.showPassword.value ? 'text' : 'password'\"\n              class=\"input-text input-pw\"\n              :class=\"{\n                'input-error': passwordConfirm.isError.value,\n                'input-shake': passwordConfirm.shakeKey.value > 0,\n              }\"\n              :key=\"`password-confirm-${passwordConfirm.shakeKey.value}`\"\n              id=\"password-confirm\"\n              placeholder=\"새로운 비밀번호 확인\"\n              v-model=\"passwordConfirm.value.value\"\n              @focus=\"passwordConfirm.handleFocus\"\n              @blur=\"passwordConfirm.handleBlur\"\n            />\n            <button\n              v-show=\"passwordConfirm.value.value.length > 0\"\n              type=\"button\"\n              class=\"mypage__info-eye\"\n              :class=\"\n                passwordConfirm.showPassword.value\n                  ? 'ico-eye-active'\n                  : 'ico-eye'\n              \"\n              @click=\"passwordConfirm.togglePassword\"\n            >\n              <span class=\"blind\">비밀번호 보기</span>\n            </button>\n          </div>\n          <Transition name=\"error\">\n            <span\n              v-if=\"passwordConfirm.isError.value\"\n              class=\"mypage__info-error\"\n            >\n              {{ passwordConfirm.errorMessage.value }}\n            </span>\n          </Transition>\n        </div>\n        <!-- 저장 버튼 -->\n        <div class=\"mypage__btn\">\n          <button\n            class=\"btn-primary btn-md-plus\"\n            :disabled=\"isSaveDisabled\"\n            @click=\"handleSave\"\n          >\n            저장\n          </button>\n        </div>\n      </div>\n    </div>\n  </div>\n  <!-- ===== 프로필 편집 모달 ===== -->\n  <Modal ref=\"editProfileModal\" title=\"내 프로필 편집하기\" type=\"d\">\n    <template #default=\"{ close }\">\n      <div class=\"modal__content-body\">\n        <div class=\"modal__edit-profile\">\n          <!-- 이미지 크롭 영역 -->\n          <div class=\"modal__edit-profile-crop\">\n            <div class=\"modal__edit-profile-thumb\">\n              <Cropper\n                v-if=\"cropImageSrc\"\n                ref=\"cropperRef\"\n                :src=\"cropImageSrc\"\n                :stencil-props=\"{\n                  aspectRatio: 1,\n                }\"\n                :stencil-size=\"{\n                  width: 256,\n                  height: 256,\n                }\"\n                :auto-zoom=\"true\"\n                :transitions=\"false\"\n                class=\"modal__edit-profile-cropper modal__edit-profile-cropper--hidden\"\n                @change=\"handleCropChange\"\n                @ready=\"handleCropReady\"\n              />\n              <img :src=\"previewAvatar\" alt=\"\" />\n              <label\n                for=\"profile-crop\"\n                class=\"modal__edit-profile-crop-label ico-before ico-photo\"\n                role=\"button\"\n              >\n                <span class=\"blind\">프로필 변경</span>\n                <input\n                  type=\"file\"\n                  class=\"blind\"\n                  id=\"profile-crop\"\n                  accept=\"image/*\"\n                  @change=\"handleFileSelect\"\n                />\n              </label>\n            </div>\n          </div>\n          <!-- 기본 아바타 선택 영역 -->\n          <div class=\"modal__edit-profile-avatar\">\n            <strong class=\"modal__edit-profile-avatar-title\">기본아바타</strong>\n            <div class=\"modal__edit-profile-avatar-list\">\n              <div\n                v-for=\"(avatar, index) in defaultAvatars\"\n                :key=\"index\"\n                class=\"modal__edit-profile-avatar-item\"\n                :class=\"{\n                  'modal__edit-profile-avatar-item--active':\n                    selectedAvatar === avatar,\n                }\"\n                role=\"button\"\n                @click=\"selectAvatar(avatar)\"\n              >\n                <img :src=\"avatar\" alt=\"\" />\n              </div>\n            </div>\n          </div>\n        </div>\n      </div>\n      <div class=\"modal__content-footer modal__content-footer--start\">\n        <button\n          type=\"button\"\n          class=\"btn-gray btn-block btn-md-plus\"\n          @click=\"close\"\n        >\n          취소\n        </button>\n        <button\n          type=\"button\"\n          class=\"btn-primary btn-block btn-md-plus\"\n          @click=\"saveAvatar(close)\"\n        >\n          저장\n        </button>\n      </div>\n    </template>\n  </Modal>\n</template>\n\n<style scoped>\n.mypage__info-error {\n  overflow: hidden;\n}\n\n.modal__edit-profile-cropper {\n  width: 100%;\n  height: 100%;\n  max-width: 256px;\n  max-height: 256px;\n  margin: 0 auto;\n}\n\n.modal__edit-profile-cropper--hidden {\n  position: absolute;\n  opacity: 0;\n  pointer-events: none;\n  z-index: -1;\n  width: 256px;\n  height: 256px;\n}\n</style>\n"],"version":3}