225 lines
8.5 KiB
Vue
225 lines
8.5 KiB
Vue
<script setup>
|
|
import { computed } from 'vue'
|
|
|
|
const props = defineProps({
|
|
user: {
|
|
type: Object,
|
|
required: true,
|
|
},
|
|
profileForm: {
|
|
type: Object,
|
|
required: true,
|
|
},
|
|
passwordForm: {
|
|
type: Object,
|
|
required: true,
|
|
},
|
|
profileBusy: {
|
|
type: Boolean,
|
|
default: false,
|
|
},
|
|
passwordBusy: {
|
|
type: Boolean,
|
|
default: false,
|
|
},
|
|
profileMessage: {
|
|
type: String,
|
|
default: '',
|
|
},
|
|
passwordMessage: {
|
|
type: String,
|
|
default: '',
|
|
},
|
|
guideTooltipResetMessage: {
|
|
type: String,
|
|
default: '',
|
|
},
|
|
carryoverCheckPolicy: {
|
|
type: String,
|
|
default: 'ask',
|
|
},
|
|
})
|
|
|
|
const emit = defineEmits([
|
|
'update:profile-field',
|
|
'update:password-field',
|
|
'submit:profile',
|
|
'submit:password',
|
|
'reset-guide-tooltips',
|
|
'update:carryover-check-policy',
|
|
])
|
|
|
|
const initials = computed(() =>
|
|
`${props.user.nickname?.slice(0, 1) ?? ''}${props.user.email?.slice(0, 1) ?? ''}`.toUpperCase(),
|
|
)
|
|
|
|
function updateProfileField(field, event) {
|
|
emit('update:profile-field', {
|
|
field,
|
|
value: event.target.value,
|
|
})
|
|
}
|
|
|
|
function updatePasswordField(field, event) {
|
|
emit('update:password-field', {
|
|
field,
|
|
value: event.target.value,
|
|
})
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<section class="grid gap-6 xl:grid-cols-[360px_minmax(0,1fr)]">
|
|
<aside class="rounded-[28px] border border-white/60 bg-white/70 p-6">
|
|
<p class="text-[11px] font-bold uppercase tracking-[0.28em] text-stone-500">Settings</p>
|
|
<div class="mt-6 flex items-center gap-4">
|
|
<div class="flex h-20 w-20 items-center justify-center rounded-full bg-stone-900 text-2xl font-bold tracking-[0.04em] text-white">
|
|
{{ initials || 'U' }}
|
|
</div>
|
|
<div>
|
|
<p class="text-xl font-semibold tracking-[-0.04em] text-stone-900">{{ user.nickname }}</p>
|
|
<p class="mt-1 text-sm font-semibold text-stone-500">{{ user.email }}</p>
|
|
</div>
|
|
</div>
|
|
<!-- <div class="mt-6 space-y-3 rounded-[24px] border border-stone-200 bg-[#fbf7f0] p-4">
|
|
<p class="text-[10px] font-bold tracking-[0.18em] text-stone-500">PROFILE NOTE</p>
|
|
<p class="text-sm font-semibold leading-6 text-stone-700">
|
|
...
|
|
</p>
|
|
</div> -->
|
|
|
|
<div class="mt-6 rounded-[24px] border border-stone-200 bg-white/80 p-4">
|
|
<p class="text-[10px] font-bold tracking-[0.18em] text-stone-500">GUIDE TOOLTIPS</p>
|
|
<p class="mt-3 text-sm font-semibold leading-6 text-stone-700">
|
|
숨긴 가이드 툴팁을 다시 표시합니다.
|
|
</p>
|
|
<button
|
|
type="button"
|
|
class="mt-4 rounded-full border border-stone-900 px-4 py-3 text-xs font-bold tracking-[0.14em] text-stone-900 transition hover:bg-stone-900 hover:text-white"
|
|
@click="emit('reset-guide-tooltips')"
|
|
>
|
|
가이드 다시 보기
|
|
</button>
|
|
<p
|
|
v-if="guideTooltipResetMessage"
|
|
class="mt-3 rounded-2xl border border-stone-200 bg-[#fbf7f0] px-4 py-3 text-xs font-semibold leading-5 text-stone-600"
|
|
>
|
|
{{ guideTooltipResetMessage }}
|
|
</p>
|
|
</div>
|
|
|
|
<div class="mt-4 rounded-[24px] border border-stone-200 bg-white/80 p-4">
|
|
<p class="text-[10px] font-bold tracking-[0.18em] text-stone-500">CARRYOVER CHECK</p>
|
|
<p class="mt-3 text-sm font-semibold leading-6 text-stone-700">
|
|
이월된 할 일을 완료할 때 이전 날짜 항목까지 함께 체크할지 정합니다.
|
|
</p>
|
|
<div class="mt-4 grid gap-2">
|
|
<button
|
|
v-for="option in [
|
|
{ value: 'ask', label: '항상 물어보기' },
|
|
{ value: 'all', label: '항상 이전까지 체크' },
|
|
{ value: 'current', label: '항상 오늘만 체크' },
|
|
]"
|
|
:key="option.value"
|
|
type="button"
|
|
class="rounded-2xl border px-4 py-3 text-left text-xs font-bold tracking-[0.12em] transition"
|
|
:class="carryoverCheckPolicy === option.value ? 'border-stone-900 bg-stone-900 text-white' : 'border-stone-200 bg-white text-stone-600 hover:border-stone-400 hover:text-stone-900'"
|
|
@click="emit('update:carryover-check-policy', option.value)"
|
|
>
|
|
{{ option.label }}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</aside>
|
|
|
|
<div class="grid gap-6">
|
|
<form class="rounded-[28px] border border-white/60 bg-white/75 p-6" @submit.prevent="emit('submit:profile')">
|
|
<p class="text-[11px] font-bold uppercase tracking-[0.24em] text-stone-500">Account Profile</p>
|
|
<div class="mt-5 grid gap-4 md:grid-cols-2">
|
|
<div class="space-y-2">
|
|
<label class="text-[11px] font-bold tracking-[0.16em] text-stone-600">닉네임</label>
|
|
<input
|
|
:value="profileForm.nickname"
|
|
type="text"
|
|
class="w-full rounded-2xl border border-stone-300 bg-white px-4 py-3 text-sm font-semibold text-stone-800 outline-none transition focus:border-stone-500"
|
|
@input="updateProfileField('nickname', $event)"
|
|
/>
|
|
</div>
|
|
<div class="space-y-2">
|
|
<label class="text-[11px] font-bold tracking-[0.16em] text-stone-600">이메일</label>
|
|
<input
|
|
:value="profileForm.email"
|
|
type="email"
|
|
class="w-full rounded-2xl border border-stone-300 bg-white px-4 py-3 text-sm font-semibold text-stone-800 outline-none transition focus:border-stone-500"
|
|
@input="updateProfileField('email', $event)"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<p
|
|
v-if="profileMessage"
|
|
class="mt-4 rounded-2xl border border-stone-300 bg-white/80 px-4 py-3 text-sm font-semibold leading-6 text-stone-700"
|
|
>
|
|
{{ profileMessage }}
|
|
</p>
|
|
|
|
<button
|
|
type="submit"
|
|
class="mt-5 rounded-full bg-stone-900 px-5 py-3 text-xs font-bold tracking-[0.18em] text-white transition hover:bg-stone-700 disabled:cursor-not-allowed disabled:bg-stone-400"
|
|
:disabled="profileBusy"
|
|
>
|
|
{{ profileBusy ? '저장 중...' : '프로필 저장' }}
|
|
</button>
|
|
</form>
|
|
|
|
<form class="rounded-[28px] border border-white/60 bg-white/75 p-6" @submit.prevent="emit('submit:password')">
|
|
<p class="text-[11px] font-bold uppercase tracking-[0.24em] text-stone-500">Password</p>
|
|
<div class="mt-5 grid gap-4 md:grid-cols-2">
|
|
<div class="space-y-2 md:col-span-2">
|
|
<label class="text-[11px] font-bold tracking-[0.16em] text-stone-600">현재 비밀번호</label>
|
|
<input
|
|
:value="passwordForm.currentPassword"
|
|
type="password"
|
|
class="w-full rounded-2xl border border-stone-300 bg-white px-4 py-3 text-sm font-semibold text-stone-800 outline-none transition focus:border-stone-500"
|
|
@input="updatePasswordField('currentPassword', $event)"
|
|
/>
|
|
</div>
|
|
<div class="space-y-2">
|
|
<label class="text-[11px] font-bold tracking-[0.16em] text-stone-600">새 비밀번호</label>
|
|
<input
|
|
:value="passwordForm.newPassword"
|
|
type="password"
|
|
class="w-full rounded-2xl border border-stone-300 bg-white px-4 py-3 text-sm font-semibold text-stone-800 outline-none transition focus:border-stone-500"
|
|
@input="updatePasswordField('newPassword', $event)"
|
|
/>
|
|
</div>
|
|
<div class="space-y-2">
|
|
<label class="text-[11px] font-bold tracking-[0.16em] text-stone-600">새 비밀번호 확인</label>
|
|
<input
|
|
:value="passwordForm.confirmPassword"
|
|
type="password"
|
|
class="w-full rounded-2xl border border-stone-300 bg-white px-4 py-3 text-sm font-semibold text-stone-800 outline-none transition focus:border-stone-500"
|
|
@input="updatePasswordField('confirmPassword', $event)"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<p
|
|
v-if="passwordMessage"
|
|
class="mt-4 rounded-2xl border border-stone-300 bg-white/80 px-4 py-3 text-sm font-semibold leading-6 text-stone-700"
|
|
>
|
|
{{ passwordMessage }}
|
|
</p>
|
|
|
|
<button
|
|
type="submit"
|
|
class="mt-5 rounded-full border border-stone-900 px-5 py-3 text-xs font-bold tracking-[0.18em] text-stone-900 transition hover:bg-stone-900 hover:text-white disabled:cursor-not-allowed disabled:border-stone-300 disabled:text-stone-400"
|
|
:disabled="passwordBusy"
|
|
>
|
|
{{ passwordBusy ? '변경 중...' : '비밀번호 변경' }}
|
|
</button>
|
|
</form>
|
|
</div>
|
|
</section>
|
|
</template>
|