Compare commits

...

1 Commits

Author SHA1 Message Date
ec9a334035 v0.1.50 - 인증 재전송 버튼 조건 보정 2026-04-24 11:55:29 +09:00
4 changed files with 10 additions and 5 deletions

View File

@@ -4,7 +4,7 @@
- 프로젝트명: 10 Minute Planner 웹 UI - 프로젝트명: 10 Minute Planner 웹 UI
- 기술 스택: Vue 3 + Vite + TailwindCSS + JavaScript - 기술 스택: Vue 3 + Vite + TailwindCSS + JavaScript
- 현재 기준 버전: `v0.1.49` 준비 중 - 현재 기준 버전: `v0.1.50` 준비 중
- Git 원격 저장소: `https://git.sori.studio/zenn/planner.sori.studio.git` - Git 원격 저장소: `https://git.sori.studio/zenn/planner.sori.studio.git`
## 기준 디자인 ## 기준 디자인
@@ -231,6 +231,7 @@
- 프론트는 `/verify-email?token=...` 진입 시 인증을 바로 확정하고 로그인 모달에 결과 메시지를 띄운다. `/reset-password?token=...`은 기존처럼 비밀번호 재설정 모달을 연다. - 프론트는 `/verify-email?token=...` 진입 시 인증을 바로 확정하고 로그인 모달에 결과 메시지를 띄운다. `/reset-password?token=...`은 기존처럼 비밀번호 재설정 모달을 연다.
- 로그인 모달에는 `이메일 인증 메일 다시 보내기` 버튼이 추가되었다. 이메일 주소를 입력한 상태에서 바로 인증 메일 재전송을 요청할 수 있어, 가입 후 메일을 못 받은 사용자가 로그인 단계에서 막히지 않게 했다. - 로그인 모달에는 `이메일 인증 메일 다시 보내기` 버튼이 추가되었다. 이메일 주소를 입력한 상태에서 바로 인증 메일 재전송을 요청할 수 있어, 가입 후 메일을 못 받은 사용자가 로그인 단계에서 막히지 않게 했다.
- 인증 메일 재전송 버튼은 항상 보이지 않는다. 로그인 모달에서 이메일 인증 관련 안내 메시지가 보일 때만 메시지 박스 오른쪽에 함께 노출한다. - 인증 메일 재전송 버튼은 항상 보이지 않는다. 로그인 모달에서 이메일 인증 관련 안내 메시지가 보일 때만 메시지 박스 오른쪽에 함께 노출한다.
- 인증 메일 재전송 버튼은 현재 `이메일 인증을 완료한 뒤 로그인해 주세요.` 안내가 있을 때만 표시한다. 인증 완료 메시지나 일반 안내 문구에서는 보이지 않도록 조건을 좁혔다.
- SETTINGS 화면에 일반 사용자 전용 `회원 탈퇴` 카드가 추가되었다. 현재 비밀번호 확인 후 계정, 플래너 기록, 목표, 세션, 인증 토큰이 함께 삭제된다. 기본 관리자 계정은 이 경로에서 삭제하지 못하게 막는다. - SETTINGS 화면에 일반 사용자 전용 `회원 탈퇴` 카드가 추가되었다. 현재 비밀번호 확인 후 계정, 플래너 기록, 목표, 세션, 인증 토큰이 함께 삭제된다. 기본 관리자 계정은 이 경로에서 삭제하지 못하게 막는다.
- `/api/auth/logout`이 추가되어 로그아웃 시 프론트 저장 토큰만 지우는 것이 아니라 서버 세션도 함께 폐기한다. - `/api/auth/logout`이 추가되어 로그아웃 시 프론트 저장 토큰만 지우는 것이 아니라 서버 세션도 함께 폐기한다.
- SETTINGS 화면 왼쪽 카드에 현재 기기 로그인 유지 방식(`로그인 유지` 또는 브라우저 세션만 유지), 최근 로그인 시각, 이메일 인증 상태를 보여준다. - SETTINGS 화면 왼쪽 카드에 현재 기기 로그인 유지 방식(`로그인 유지` 또는 브라우저 세션만 유지), 최근 로그인 시각, 이메일 인증 상태를 보여준다.

4
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{ {
"name": "ten-minute-planner", "name": "ten-minute-planner",
"version": "0.1.49", "version": "0.1.50",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "ten-minute-planner", "name": "ten-minute-planner",
"version": "0.1.49", "version": "0.1.50",
"dependencies": { "dependencies": {
"vue": "^3.5.13" "vue": "^3.5.13"
}, },

View File

@@ -1,7 +1,7 @@
{ {
"name": "ten-minute-planner", "name": "ten-minute-planner",
"private": true, "private": true,
"version": "0.1.49", "version": "0.1.50",
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",

View File

@@ -11,6 +11,7 @@ import StatsDashboard from './components/StatsDashboard.vue'
import { import {
deleteAdminUser, deleteAdminUser,
fetchAdminOverview, fetchAdminOverview,
fetchAdminUserDetail,
revokeAdminUserSessions, revokeAdminUserSessions,
updateAdminUserStatus, updateAdminUserStatus,
} from './lib/adminApi' } from './lib/adminApi'
@@ -114,6 +115,7 @@ const hiddenGuideTooltips = ref(readHiddenGuideTooltips())
const ddayDisabledDateKeys = ref(readDdayDisabledDateKeys()) const ddayDisabledDateKeys = ref(readDdayDisabledDateKeys())
const adminBusy = ref(false) const adminBusy = ref(false)
const adminActionUserId = ref(null) const adminActionUserId = ref(null)
const adminDetailBusy = ref(false)
const adminMessage = ref('') const adminMessage = ref('')
const adminOverview = ref({ const adminOverview = ref({
totalUsers: 0, totalUsers: 0,
@@ -127,6 +129,8 @@ const adminOverview = ref({
}) })
const adminUsers = ref([]) const adminUsers = ref([])
const adminRecentLogins = ref([]) const adminRecentLogins = ref([])
const adminSelectedUserId = ref(null)
const adminUserDetail = ref(null)
const hours = [ const hours = [
'6', '7', '8', '9', '10', '11', '12', '6', '7', '8', '9', '10', '11', '12',
@@ -653,7 +657,7 @@ const showVerificationResend = computed(() => {
return false return false
} }
return authMessage.value.includes('이메일 인증') return authMessage.value.includes('이메일 인증을 완료한 뒤 로그인해 주세요.')
}) })
const filteredGoals = computed(() => { const filteredGoals = computed(() => {
const query = goalQuery.value.trim().toLowerCase() const query = goalQuery.value.trim().toLowerCase()