@@ -0,0 +1,32 @@ | |||||
<template> | |||||
<div class="entries"> | |||||
<span class="entry" v-for="entry in entries" | |||||
@click="$emit('select', entry.value)" | |||||
>{{entry.text}}</span> | |||||
</div> | |||||
</template> | |||||
<style scoped> | |||||
.entries { | |||||
display: flex; | |||||
flex-flow: column; | |||||
position: absolute; | |||||
left: 0; | |||||
background: var(--secondary-bg); | |||||
width: 100%; | |||||
margin-top: 5px; | |||||
z-index: 3; | |||||
border: 1px solid black; | |||||
} | |||||
.entry { | |||||
border-bottom: 1px solid var(--text); | |||||
padding: 5px; | |||||
cursor: pointer; | |||||
} | |||||
</style> | |||||
<script setup> | |||||
const props = defineProps(['entries']) | |||||
const emits = defineEmits(['select']) | |||||
</script> |
@@ -38,6 +38,16 @@ | |||||
<option value="Canada">Canada</option> | <option value="Canada">Canada</option> | ||||
</select> | </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> | <button @click="saveProfile">Save</button> | ||||
</section> | </section> | ||||
@@ -67,6 +77,7 @@ | |||||
<script setup> | <script setup> | ||||
import { ref, watch, onMounted } from "vue" | import { ref, watch, onMounted } from "vue" | ||||
import Dialog from "./dialog.vue" | import Dialog from "./dialog.vue" | ||||
import Dropdown from "./dropdown.vue" | |||||
let avatar = ref(null) // the canvas element | let avatar = ref(null) // the canvas element | ||||
let letterhead = ref(null) // the canvas element | let letterhead = ref(null) // the canvas element | ||||
@@ -78,6 +89,9 @@ let passError = ref('') | |||||
let oldPass = ref('') | let oldPass = ref('') | ||||
let newPass = ref('') | let newPass = ref('') | ||||
let confirmPass = ref('') | let confirmPass = ref('') | ||||
const locationsId = ref(null) | |||||
const addresses = ref([]) | |||||
const address = ref({}) | |||||
const props = defineProps(['user', 'token']) | const props = defineProps(['user', 'token']) | ||||
const emit = defineEmits(['updateAvatar', 'updateLetterhead']) | const emit = defineEmits(['updateAvatar', 'updateLetterhead']) | ||||
let user = Object.assign({}, props.user) | let user = Object.assign({}, props.user) | ||||
@@ -176,8 +190,19 @@ function changeLetterhead(blob) { | |||||
}) | }) | ||||
} | } | ||||
function setAddress(a) { | |||||
addresses.value = null | |||||
address.value = { | |||||
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, | |||||
} | |||||
} | |||||
function saveProfile() { | function saveProfile() { | ||||
console.log(user.firstName) | |||||
fetch(`/api/user`, | fetch(`/api/user`, | ||||
{method: 'PATCH', | {method: 'PATCH', | ||||
body: JSON.stringify(user), | body: JSON.stringify(user), | ||||
@@ -216,6 +241,31 @@ function changePassword(f) { | |||||
}) | }) | ||||
} | } | ||||
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) => { | watch(props.user, (u) => { | ||||
if (props.user.avatar) { | if (props.user.avatar) { | ||||
changeAvatar(props.user.avatar) | changeAvatar(props.user.avatar) | ||||
@@ -226,4 +276,13 @@ watch(props.user, (u) => { | |||||
} | } | ||||
}, {immediate: true}) | }, {immediate: true}) | ||||
onMounted(() => { | |||||
}) | |||||
</script> | </script> | ||||
<style scoped> | |||||
div.address-entry input { | |||||
width: 100%; | |||||
} | |||||
</style> |
@@ -1,9 +0,0 @@ | |||||
/* | |||||
package main | |||||
var config = map[string]string { | |||||
"dbUsername": "", | |||||
"dbPassword": "", | |||||
} | |||||
*/ | |||||
package main |
@@ -2,6 +2,7 @@ | |||||
CREATE TABLE address ( | CREATE TABLE address ( | ||||
id INT AUTO_INCREMENT, | id INT AUTO_INCREMENT, | ||||
full VARCHAR(200) UNIQUE NOT NULL, | |||||
street VARCHAR(40) UNIQUE NOT NULL, | street VARCHAR(40) UNIQUE NOT NULL, | ||||
city VARCHAR(40) UNIQUE NOT NULL, | city VARCHAR(40) UNIQUE NOT NULL, | ||||
region VARCHAR(40) UNIQUE NOT NULL, | region VARCHAR(40) UNIQUE NOT NULL, | ||||
@@ -11,6 +11,7 @@ | |||||
}, | }, | ||||
"devDependencies": { | "devDependencies": { | ||||
"css-loader": "^6.7.1", | "css-loader": "^6.7.1", | ||||
"dotenv-webpack": "^8.0.1", | |||||
"vue-loader": "^17.0.1", | "vue-loader": "^17.0.1", | ||||
"vue-template-compiler": "^2.7.13", | "vue-template-compiler": "^2.7.13", | ||||
"webpack": "^5.74.0", | "webpack": "^5.74.0", | ||||
@@ -1277,6 +1278,39 @@ | |||||
"integrity": "sha512-kxxKlPEDa6Nc5WJi+qRgPbOAbgTpSULL+vI3NUXsZMlkJxTqYI9wg5ZTay2sFrdZRWHPWNi+EdAhcJf81WtoMQ==", | "integrity": "sha512-kxxKlPEDa6Nc5WJi+qRgPbOAbgTpSULL+vI3NUXsZMlkJxTqYI9wg5ZTay2sFrdZRWHPWNi+EdAhcJf81WtoMQ==", | ||||
"optional": true | "optional": true | ||||
}, | }, | ||||
"node_modules/dotenv": { | |||||
"version": "8.6.0", | |||||
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.6.0.tgz", | |||||
"integrity": "sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g==", | |||||
"dev": true, | |||||
"engines": { | |||||
"node": ">=10" | |||||
} | |||||
}, | |||||
"node_modules/dotenv-defaults": { | |||||
"version": "2.0.2", | |||||
"resolved": "https://registry.npmjs.org/dotenv-defaults/-/dotenv-defaults-2.0.2.tgz", | |||||
"integrity": "sha512-iOIzovWfsUHU91L5i8bJce3NYK5JXeAwH50Jh6+ARUdLiiGlYWfGw6UkzsYqaXZH/hjE/eCd/PlfM/qqyK0AMg==", | |||||
"dev": true, | |||||
"dependencies": { | |||||
"dotenv": "^8.2.0" | |||||
} | |||||
}, | |||||
"node_modules/dotenv-webpack": { | |||||
"version": "8.0.1", | |||||
"resolved": "https://registry.npmjs.org/dotenv-webpack/-/dotenv-webpack-8.0.1.tgz", | |||||
"integrity": "sha512-CdrgfhZOnx4uB18SgaoP9XHRN2v48BbjuXQsZY5ixs5A8579NxQkmMxRtI7aTwSiSQcM2ao12Fdu+L3ZS3bG4w==", | |||||
"dev": true, | |||||
"dependencies": { | |||||
"dotenv-defaults": "^2.0.2" | |||||
}, | |||||
"engines": { | |||||
"node": ">=10" | |||||
}, | |||||
"peerDependencies": { | |||||
"webpack": "^4 || ^5" | |||||
} | |||||
}, | |||||
"node_modules/ee-first": { | "node_modules/ee-first": { | ||||
"version": "1.1.1", | "version": "1.1.1", | ||||
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", | ||||
@@ -9,6 +9,7 @@ | |||||
}, | }, | ||||
"devDependencies": { | "devDependencies": { | ||||
"css-loader": "^6.7.1", | "css-loader": "^6.7.1", | ||||
"dotenv-webpack": "^8.0.1", | |||||
"vue-loader": "^17.0.1", | "vue-loader": "^17.0.1", | ||||
"vue-template-compiler": "^2.7.13", | "vue-template-compiler": "^2.7.13", | ||||
"webpack": "^5.74.0", | "webpack": "^5.74.0", | ||||
@@ -29,6 +29,7 @@ import ( | |||||
type Address struct { | type Address struct { | ||||
Id int `json:"id"` | Id int `json:"id"` | ||||
Full string `json:"full"` | |||||
Street string `json:"street"` | Street string `json:"street"` | ||||
City string `json:"city"` | City string `json:"city"` | ||||
Region string `json:"region"` | Region string `json:"region"` | ||||
@@ -1017,7 +1018,6 @@ func changePassword(w http.ResponseWriter, db *sql.DB, r *http.Request) { | |||||
claim, err := getClaims(r) | claim, err := getClaims(r) | ||||
err = json.NewDecoder(r.Body).Decode(&pass) | err = json.NewDecoder(r.Body).Decode(&pass) | ||||
if err != nil { http.Error(w, "Bad fields.", 422); return } | if err != nil { http.Error(w, "Bad fields.", 422); return } | ||||
fmt.Println(pass) | |||||
if checkPassword(db, claim.Id, pass.Old) { | if checkPassword(db, claim.Id, pass.Old) { | ||||
err = setPassword(db, claim.Id, pass.New) | err = setPassword(db, claim.Id, pass.New) | ||||
@@ -1,5 +1,6 @@ | |||||
const { VueLoaderPlugin } = require('vue-loader') | const { VueLoaderPlugin } = require('vue-loader') | ||||
const path = require('path'); | |||||
const path = require('path') | |||||
const Dotenv = require('dotenv-webpack') | |||||
module.exports = { | module.exports = { | ||||
entry: './main.js', | entry: './main.js', | ||||
@@ -23,5 +24,5 @@ module.exports = { | |||||
} | } | ||||
}, | }, | ||||
// Required for also applying rules to sections of SFC | // Required for also applying rules to sections of SFC | ||||
plugins: [new VueLoaderPlugin()], | |||||
plugins: [new VueLoaderPlugin(), new Dotenv({systemVars: true})], | |||||
}; | }; |