Matthew Raymer
1 week ago
48 changed files with 1366 additions and 821 deletions
File diff suppressed because it is too large
@ -0,0 +1,238 @@ |
|||
<template> |
|||
<QuickNav selected="Contacts" /> |
|||
<TopMessage /> |
|||
|
|||
<section id="ContactEdit" class="p-6 max-w-3xl mx-auto"> |
|||
<div id="ViewBreadcrumb" class="mb-8"> |
|||
<h1 class="text-4xl text-center font-light relative px-7"> |
|||
<!-- Back --> |
|||
<button |
|||
@click="$router.go(-1)" |
|||
class="text-lg text-center px-2 py-1 absolute -left-2 -top-1" |
|||
> |
|||
<fa icon="chevron-left" class="fa-fw" /> |
|||
</button> |
|||
{{ contact.name || AppString.NO_CONTACT_NAME }} |
|||
</h1> |
|||
</div> |
|||
|
|||
<!-- Contact Name --> |
|||
<div class="mt-4 flex" data-testId="contactName"> |
|||
<label |
|||
for="contactName" |
|||
class="block text-sm font-medium text-gray-700 mt-2" |
|||
> |
|||
Name |
|||
</label> |
|||
<input |
|||
type="text" |
|||
class="block w-full ml-2 mt-1 border border-gray-300 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500" |
|||
v-model="contactName" |
|||
/> |
|||
</div> |
|||
|
|||
<!-- Contact Notes --> |
|||
<div class="mt-4"> |
|||
<label for="contactNotes" class="block text-sm font-medium text-gray-700"> |
|||
Notes |
|||
</label> |
|||
<textarea |
|||
id="contactNotes" |
|||
rows="4" |
|||
class="block w-full mt-1 border border-gray-300 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500" |
|||
v-model="contactNotes" |
|||
></textarea> |
|||
</div> |
|||
|
|||
<!-- Contact Methods --> |
|||
<div class="mt-4"> |
|||
<h2 class="text-lg font-medium text-gray-700">Contact Methods</h2> |
|||
<div |
|||
v-for="(method, index) in contactMethods" |
|||
:key="index" |
|||
class="flex mt-2" |
|||
> |
|||
<input |
|||
type="text" |
|||
v-model="method.label" |
|||
class="block w-1/4 border border-gray-300 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500" |
|||
placeholder="Label" |
|||
/> |
|||
<input |
|||
type="text" |
|||
v-model="method.type" |
|||
class="block ml-2 w-1/4 border border-gray-300 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500" |
|||
placeholder="Type" |
|||
/> |
|||
<div class="relative"> |
|||
<button |
|||
@click="toggleDropdown(index)" |
|||
class="px-2 py-1 bg-gray-200 rounded-md" |
|||
> |
|||
<fa icon="caret-down" class="fa-fw" /> |
|||
</button> |
|||
<div |
|||
v-if="dropdownIndex === index" |
|||
class="absolute bg-white border border-gray-300 rounded-md mt-1" |
|||
> |
|||
<div |
|||
@click="setMethodType(index, 'CELL')" |
|||
class="px-4 py-2 hover:bg-gray-100 cursor-pointer" |
|||
> |
|||
CELL |
|||
</div> |
|||
<div |
|||
@click="setMethodType(index, 'EMAIL')" |
|||
class="px-4 py-2 hover:bg-gray-100 cursor-pointer" |
|||
> |
|||
EMAIL |
|||
</div> |
|||
<div |
|||
@click="setMethodType(index, 'WHATSAPP')" |
|||
class="px-4 py-2 hover:bg-gray-100 cursor-pointer" |
|||
> |
|||
WHATSAPP |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<input |
|||
type="text" |
|||
v-model="method.value" |
|||
class="block ml-2 w-1/2 border border-gray-300 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500" |
|||
placeholder="Number, email, etc." |
|||
/> |
|||
<button @click="removeContactMethod(index)" class="ml-2 text-red-500"> |
|||
<fa icon="trash-can" class="fa-fw" /> |
|||
</button> |
|||
</div> |
|||
<button @click="addContactMethod" class="mt-2"> |
|||
<fa |
|||
icon="plus" |
|||
class="fa-fw px-2 py-2.5 bg-green-500 text-green-100 rounded-full" |
|||
/> |
|||
</button> |
|||
</div> |
|||
|
|||
<!-- Save Button --> |
|||
<div class="mt-8 flex justify-between"> |
|||
<button |
|||
class="px-4 py-2 bg-blue-500 text-white rounded-md" |
|||
@click="saveEdit" |
|||
> |
|||
Save |
|||
</button> |
|||
<button |
|||
class="ml-4 px-4 py-2 bg-slate-500 text-white rounded-md" |
|||
@click="$router.go(-1)" |
|||
> |
|||
Cancel |
|||
</button> |
|||
</div> |
|||
</section> |
|||
</template> |
|||
|
|||
<script lang="ts"> |
|||
import * as R from "ramda"; |
|||
import { Component, Vue } from "vue-facing-decorator"; |
|||
import { RouteLocation, Router } from "vue-router"; |
|||
|
|||
import QuickNav from "../components/QuickNav.vue"; |
|||
import TopMessage from "../components/TopMessage.vue"; |
|||
import { AppString, NotificationIface } from "../constants/app"; |
|||
import { db } from "../db/index"; |
|||
import { Contact, ContactMethod } from "../db/tables/contacts"; |
|||
|
|||
@Component({ |
|||
components: { |
|||
QuickNav, |
|||
TopMessage, |
|||
}, |
|||
}) |
|||
export default class ContactEditView extends Vue { |
|||
$notify!: (notification: NotificationIface, timeout?: number) => void; |
|||
|
|||
contact: Contact = { |
|||
did: "", |
|||
name: "", |
|||
notes: "", |
|||
}; |
|||
contactName = ""; |
|||
contactNotes = ""; |
|||
contactMethods: Array<ContactMethod> = []; |
|||
dropdownIndex: number | null = null; |
|||
|
|||
AppString = AppString; |
|||
|
|||
async created() { |
|||
const contactDid = (this.$route as RouteLocation).params.did; |
|||
const contact = await db.contacts.get(contactDid || ""); |
|||
if (contact) { |
|||
this.contact = contact; |
|||
this.contactName = contact.name || ""; |
|||
this.contactNotes = contact.notes || ""; |
|||
this.contactMethods = contact.contactMethods || []; |
|||
} else { |
|||
this.$notify({ |
|||
group: "alert", |
|||
type: "danger", |
|||
title: "Contact Not Found", |
|||
text: "There is no contact with DID " + contactDid, |
|||
}); |
|||
(this.$router as Router).push({ path: "/contacts" }); |
|||
return; |
|||
} |
|||
} |
|||
|
|||
addContactMethod() { |
|||
this.contactMethods.push({ label: "", type: "", value: "" }); |
|||
} |
|||
|
|||
removeContactMethod(index: number) { |
|||
this.contactMethods.splice(index, 1); |
|||
} |
|||
|
|||
toggleDropdown(index: number) { |
|||
this.dropdownIndex = this.dropdownIndex === index ? null : index; |
|||
} |
|||
|
|||
setMethodType(index: number, type: string) { |
|||
this.contactMethods[index].type = type; |
|||
this.dropdownIndex = null; |
|||
} |
|||
|
|||
async saveEdit() { |
|||
// without this conversion, "Failed to execute 'put' on 'IDBObjectStore': [object Array] could not be cloned." |
|||
const contactMethodsObj = JSON.parse(JSON.stringify(this.contactMethods)); |
|||
const contactMethods = contactMethodsObj.map((method: ContactMethod) => |
|||
R.set(R.lensProp("type"), method.type.toUpperCase(), method), |
|||
); |
|||
if (!R.equals(contactMethodsObj, contactMethods)) { |
|||
this.contactMethods = contactMethods; |
|||
this.$notify( |
|||
{ |
|||
group: "alert", |
|||
type: "warning", |
|||
title: "Contact Methods Updated", |
|||
text: "Note that some methods have been updated, such as uppercasing 'email' to 'EMAIL'. Save again if the changes are acceptable.", |
|||
}, |
|||
15000, |
|||
); |
|||
return; |
|||
} |
|||
await db.contacts.update(this.contact.did, { |
|||
name: this.contactName, |
|||
notes: this.contactNotes, |
|||
contactMethods: contactMethods, |
|||
}); |
|||
this.$notify({ |
|||
group: "alert", |
|||
type: "success", |
|||
title: "Contact Saved", |
|||
text: "The contact info has been updated successfully.", |
|||
}); |
|||
(this.$router as Router).push({ |
|||
path: "/did/" + encodeURIComponent(this.contact.did), |
|||
}); |
|||
} |
|||
} |
|||
</script> |
Loading…
Reference in new issue