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