- <template>
- <div class="page settings">
- <h2>Settings</h2>
-
- <section class="form inputs">
- <h3>Avatar</h3>
- <canvas class="displayer" width="200" height="200" ref="avatar"></canvas>
- <input type="file"
- @change="e => changeAvatar(e.target.files[0])"
- />
- <button @click="uploadAvatar">Upload</button>
- <label class="error">{{avatarError}}</label>
- </section>
-
- <section class="form inputs letterhead">
- <h3>Letterhead</h3>
- <canvas class="displayer" height="200" ref="letterhead"></canvas>
- <input type="file"
- @change="e => {setLetterhead(e.target.files[0])}"
- />
- <button @click="uploadLetterhead">Upload</button>
- <label class="error">{{letterheadError}}</label>
- </section>
-
- <section class="form inputs special">
- <h3>Profile</h3>
- <label for="">First Name</label>
- <input type="text" v-model="user.firstName">
- <label for="">Last Name</label>
- <input type="text" :value="user.lastName">
- <label for="">NMLS ID</label>
- <input type="text">
- <label for="">Branch ID</label>
- <input type="text" :value="user.branchId" disabled>
-
- <select id="" name="" :value="user.country">
- <option value="USA">USA</option>
- <option value="Canada">Canada</option>
- </select>
-
- <div class="address-entry">
- <label for="">Address</label>
- <input type="text" @input="searchLocation" :value="address.full">
- <dropdown v-if="addresses && addresses.length"
- :entries="addresses.map(a => ({text: a.full_address, value: a}))"
- @select="setAddress"
- >
- </dropdown>
- </div>
-
- <button @click="saveProfile">Save</button>
- </section>
-
- <section class="form inputs special">
- <h3>Reset Password</h3>
- <label for="">Old Password</label>
- <input type="password" v-model="oldPass">
- <label for="">New Password</label>
- <input type="password" v-model="newPass">
- <label for="">Confirm Password</label>
- <input type="password" v-model="confirmPass">
- <button
- :disabled="!(oldPass && newPass && confirmPass)"
- @click="changePassword">Save</button>
- <label class="error">{{passError}}</label>
- </section>
-
- <Dialog v-if="ready" @close="() => ready = false">
- <h3>Confirm your current password to save changes.</h3>
- <input type="text">
- <button>Confirm</button>
- </Dialog>
-
- </div>
- </template>
-
- <script setup>
- import { ref, watch, onMounted } from "vue"
- import Dialog from "./dialog.vue"
- import Dropdown from "./dropdown.vue"
-
- let avatar = ref(null) // the canvas element
- let letterhead = ref(null) // the canvas element
- let ready = ref(false)
- let avatarChanged = ref(false)
- let avatarError = ref('')
- let letterheadError = ref('')
- let passError = ref('')
- let oldPass = ref('')
- let newPass = ref('')
- let confirmPass = ref('')
- const locationsId = ref(null)
- const addresses = ref([])
- const address = ref({})
- const props = defineProps(['user', 'token'])
- const emit = defineEmits(['updateAvatar', 'updateLetterhead'])
- const user = Object.assign({}, props.user)
-
- function save() {
- }
-
- function check() {
- ready.value = true
- }
-
- function uploadAvatar() {
- avatar.value.toBlob(b => {
- fetch(`/api/user/avatar`,
- {method: 'POST',
- body: b,
- headers: {
- "Accept": "application/json",
- "Authorization": `Bearer ${props.token}`,
- },
- }).then(resp => {
- if (resp.ok) {emit('updateAvatar')}
- })
- })
- }
-
- function uploadLetterhead() {
- letterhead.value.toBlob(b => {
- fetch(`/api/user/letterhead`,
- {method: 'POST',
- body: b,
- headers: {
- "Accept": "application/json",
- "Authorization": `Bearer ${props.token}`,
- },
- }).then(resp => {
- if (resp.ok) {emit('updateLetterhead')}
- })
- })
- }
-
- function setLetterhead(f) {
- letterheadError.value = ""
- const validTypes = ['image/jpeg', 'image/png']
-
- if (!validTypes.includes(f.type)) {
- letterheadError.value = 'Image must be JPEG of PNG format'
- return
- }
- fetch(`/api/letterhead`,
- {method: 'POST',
- body: f,
- headers: {
- "Accept": "application/json",
- "Authorization": `Bearer ${props.token}`,
- },
- }).then(resp => {
- if (resp.ok) {
- resp.blob().then(b => changeLetterhead(b))
- } else {
- resp.text().then(e => letterheadError.value = e)
- }
- })
- }
-
- function changeAvatar(blob) {
- const validTypes = ['image/jpeg', 'image/png']
-
- if (!validTypes.includes(blob?.type)) {
- avatarError.value = 'Image must be JPEG of PNG format'
- return
- }
-
- avatarError.value = ''
-
- createImageBitmap(blob,
- {resizeWidth: 200, resizeHeight: 200, resizeQuality: 'medium'}).
- then((img) => {
- avatar.value.getContext("2d").drawImage(img, 0, 0, 200, 200)
- })
- }
-
- function changeLetterhead(blob) {
- const validTypes = ['image/jpeg', 'image/png']
-
- if (!validTypes.includes(blob.type)) {
- letterheadError.value = 'Image must be JPEG of PNG format'
- return
- }
-
- createImageBitmap(blob).
- then((img) => {
- let ctx = letterhead.value.getContext("2d")
- ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height)
- ctx.drawImage(img, 0, 0)
- })
- }
-
- function setAddress(a) {
-
- address.value = {
- id: address.value.id,
- full: a.full_address,
- street: a.address,
- city: a.context?.place.name ?? '',
- region: a.context?.region?.name ?? '',
- country: a.context?.country?.name ?? '',
- zip: a.context?.postcode?.name ?? '',
- }
- addresses.value = null
- }
-
- function saveProfile() {
- user.value.address = address.value
- fetch(`/api/user`,
- {method: 'PATCH',
- body: JSON.stringify(user.value),
- headers: {
- "Accept": "application/json",
- "Authorization": `Bearer ${props.token}`,
- },
- }).then(resp => {
- if (resp.ok) {}
- })
- }
-
- function changePassword(f) {
- fetch(`/api/user/password`,
- {method: 'POST',
- body: JSON.stringify({
- old: oldPass.value,
- new: newPass.value,
- confirm: confirmPass.value,
- }),
- headers: {
- "Accept": "application/json",
- "Authorization": `Bearer ${props.token}`,
- },
- }).then(resp => {
- if (resp.ok) {
- resp.blob().then(b => {
- oldPass.value = ""
- newPass.value = ""
- confirmPass.value = ""
- passError.value = ""
- })
- } else {
- resp.text().then(e => passError.value = e)
- }
- })
- }
-
- function searchLocation(e) {
- address.value.full = e.target.value
- clearTimeout(locationsId.value)
- locationsId.value = setTimeout(getLocations, 1000, e)
- }
-
- function getLocations(e) {
- let prefix = "https://api.mapbox.com/search/searchbox/v1/suggest?"
- let search = e.target.value
- let key = encodeURIComponent(process.env.MAPBOX_API_KEY)
-
- if (!search) return
-
- fetch(`${prefix}q=${search}&proximity=ip&access_token=${key}&session_token=1`
- ).then(resp => resp.json()).then(entries => {
- if (!entries?.suggestions) {
- addresses.value = null
- return
- }
-
- addresses.value = entries.suggestions.filter(e => e.full_address)
- })
- }
-
-
- watch(props.user, (u) => {
- if (props.user.avatar) {
- changeAvatar(props.user.avatar)
- }
-
- if (props.user.letterhead) {
- changeLetterhead(props.user.letterhead)
- }
- address.value = Object.assign({}, props.user.address)
- user.value = Object.assign({}, props.user)
- user.value.address = address.value
- }, {immediate: true})
-
- </script>
-
- <style scoped>
- div.address-entry {
- display: flex;
- flex-flow: column;
- position: relative;
- }
- </style>
|