allow application of labels to contacts that are imported
This commit is contained in:
@@ -36,7 +36,97 @@
|
||||
Make my activity visible to new contacts.
|
||||
</span>
|
||||
|
||||
<div v-if="sameCount > 0">
|
||||
<!-- Labels Section -->
|
||||
<div
|
||||
v-if="contactsImporting"
|
||||
class="mt-4 p-4 border-2 border-slate-700 rounded"
|
||||
>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-2">
|
||||
Labels for Imported Contacts
|
||||
</label>
|
||||
|
||||
<!-- Apply to existing contacts checkbox -->
|
||||
<div v-if="sameCount > 0" class="mb-3 flex items-center">
|
||||
<input
|
||||
id="applyLabelsToExisting"
|
||||
v-model="applyLabelsToExisting"
|
||||
type="checkbox"
|
||||
class="mr-2"
|
||||
/>
|
||||
<label
|
||||
for="applyLabelsToExisting"
|
||||
class="text-sm text-gray-700 cursor-pointer"
|
||||
>
|
||||
Apply these labels to existing contacts (contacts that are already
|
||||
in your list)
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<!-- Selected Labels -->
|
||||
<div class="flex flex-wrap gap-2 mb-3">
|
||||
<span
|
||||
v-for="label in selectedLabels"
|
||||
:key="label"
|
||||
class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-blue-100 text-blue-800"
|
||||
>
|
||||
{{ label }}
|
||||
<button
|
||||
type="button"
|
||||
class="flex-shrink-0 ml-1.5 inline-flex text-blue-400 hover:text-blue-600 focus:outline-none"
|
||||
@click="removeSelectedLabel(label)"
|
||||
>
|
||||
<font-awesome icon="xmark" class="h-4 w-4" />
|
||||
</button>
|
||||
</span>
|
||||
<span
|
||||
v-if="selectedLabels.length === 0"
|
||||
class="text-xs text-gray-400 italic"
|
||||
>
|
||||
No labels selected
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<!-- Label Input -->
|
||||
<div class="flex gap-2 mb-3">
|
||||
<input
|
||||
v-model="newLabelInput"
|
||||
type="text"
|
||||
placeholder="Add or create a label..."
|
||||
class="block w-full border border-gray-300 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500 text-sm p-2"
|
||||
@keyup.enter="addNewLabel"
|
||||
/>
|
||||
<button
|
||||
class="px-4 py-1 bg-green-500 text-white rounded-md text-sm whitespace-nowrap"
|
||||
@click="addNewLabel"
|
||||
>
|
||||
Add Label
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Available Labels -->
|
||||
<div v-if="availableLabels.length > 0" class="mt-3">
|
||||
<p class="text-xs font-medium text-gray-700 mb-2">
|
||||
Available labels (click to toggle):
|
||||
</p>
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<button
|
||||
v-for="label in availableLabels"
|
||||
:key="label"
|
||||
class="inline-flex items-center px-3 py-1 rounded text-sm font-medium transition-colors"
|
||||
:class="
|
||||
selectedLabels.includes(label)
|
||||
? 'bg-blue-500 text-white hover:bg-blue-600'
|
||||
: 'bg-gray-100 text-gray-800 hover:bg-gray-200'
|
||||
"
|
||||
@click="toggleLabel(label)"
|
||||
>
|
||||
{{ label }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="sameCount > 0" class="mt-2">
|
||||
<span v-if="sameCount == 1"
|
||||
>One contact is the same as an existing contact</span
|
||||
>
|
||||
@@ -48,7 +138,7 @@
|
||||
<!-- Results List -->
|
||||
<ul
|
||||
v-if="contactsImporting.length > sameCount"
|
||||
class="border-t border-slate-300"
|
||||
class="mt-2 border-t border-slate-300"
|
||||
>
|
||||
<li v-for="(contact, index) in contactsImporting" :key="contact.did">
|
||||
<div
|
||||
@@ -104,11 +194,18 @@
|
||||
class="bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-sm text-white mt-2 px-2 py-1.5 rounded"
|
||||
@click="importContacts"
|
||||
>
|
||||
Import Selected Contacts
|
||||
Import Contacts
|
||||
</button>
|
||||
</ul>
|
||||
<p v-else-if="contactsImporting.length > 0">
|
||||
All those contacts are already in your list with the same information.
|
||||
<button
|
||||
v-if="applyLabelsToExisting"
|
||||
class="bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-sm text-white mt-2 px-2 py-1.5 rounded"
|
||||
@click="importContacts"
|
||||
>
|
||||
Apply Labels to Existing Contacts
|
||||
</button>
|
||||
</p>
|
||||
<div v-else>
|
||||
There are no contacts in that import. If some were sent, try again to
|
||||
@@ -312,6 +409,17 @@ export default class ContactImportView extends Vue {
|
||||
/** Count of duplicate contacts found */
|
||||
sameCount = 0;
|
||||
|
||||
// --- Labels ---
|
||||
|
||||
/** Labels selected to be applied to imported contacts */
|
||||
selectedLabels: Array<string> = [];
|
||||
/** Input for new label creation */
|
||||
newLabelInput = "";
|
||||
/** All available labels from existing contacts */
|
||||
availableLabels: Array<string> = [];
|
||||
/** Whether to apply labels to existing contacts as well */
|
||||
applyLabelsToExisting = false;
|
||||
|
||||
/**
|
||||
* Component lifecycle hook that initializes the contact import process
|
||||
*
|
||||
@@ -338,6 +446,7 @@ export default class ContactImportView extends Vue {
|
||||
this.notify = createNotifyHelpers(this.$notify);
|
||||
|
||||
await this.initializeSettings();
|
||||
await this.loadAvailableLabels();
|
||||
await this.processQueryParams();
|
||||
await this.processJwtFromPath();
|
||||
await this.handleAutoImport();
|
||||
@@ -357,6 +466,21 @@ export default class ContactImportView extends Vue {
|
||||
this.apiServer = settings.apiServer || "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads all available labels from existing contacts
|
||||
*/
|
||||
private async loadAvailableLabels() {
|
||||
try {
|
||||
this.availableLabels = await this.$getUniqueContactLabels();
|
||||
} catch (error) {
|
||||
const fullError =
|
||||
"Error loading available labels: " + errorStringForLog(error);
|
||||
this.$logAndConsole(fullError, true);
|
||||
// Don't show error to user as this is non-critical
|
||||
this.availableLabels = [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes contacts from URL query parameters
|
||||
*/
|
||||
@@ -511,6 +635,46 @@ export default class ContactImportView extends Vue {
|
||||
this.checkingImports = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new label to the selected labels list
|
||||
*/
|
||||
addNewLabel() {
|
||||
const label = this.newLabelInput.trim();
|
||||
if (label && !this.selectedLabels.includes(label)) {
|
||||
this.selectedLabels.push(label);
|
||||
// Add to available labels if it's new
|
||||
if (!this.availableLabels.includes(label)) {
|
||||
this.availableLabels.push(label);
|
||||
this.availableLabels.sort();
|
||||
}
|
||||
this.newLabelInput = "";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a label from the selected labels list
|
||||
* @param label Label to remove
|
||||
*/
|
||||
removeSelectedLabel(label: string) {
|
||||
const index = this.selectedLabels.indexOf(label);
|
||||
if (index > -1) {
|
||||
this.selectedLabels.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggles a label in the selected labels list
|
||||
* @param label Label to toggle
|
||||
*/
|
||||
toggleLabel(label: string) {
|
||||
const index = this.selectedLabels.indexOf(label);
|
||||
if (index > -1) {
|
||||
this.selectedLabels.splice(index, 1);
|
||||
} else {
|
||||
this.selectedLabels.push(label);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Imports selected contacts and sets visibility if requested
|
||||
* Updates existing contacts or adds new ones
|
||||
@@ -520,7 +684,7 @@ export default class ContactImportView extends Vue {
|
||||
let importedCount = 0,
|
||||
updatedCount = 0;
|
||||
|
||||
// Process selected contacts
|
||||
// Process selected contacts (new contacts or existing with changes)
|
||||
for (let i = 0; i < this.contactsImporting.length; i++) {
|
||||
if (this.contactsSelected[i]) {
|
||||
const contactWithLabels = this.contactsImporting[i];
|
||||
@@ -528,25 +692,61 @@ export default class ContactImportView extends Vue {
|
||||
...contactWithLabels,
|
||||
labels: undefined,
|
||||
};
|
||||
const contactLabels = contactWithLabels.labels || [];
|
||||
// Merge existing labels with selected labels for imported contacts
|
||||
const existingLabels = contactWithLabels.labels || [];
|
||||
const mergedLabels = Array.from(
|
||||
new Set([...existingLabels, ...this.selectedLabels]),
|
||||
);
|
||||
const existingContact = this.contactsExisting[contact.did];
|
||||
|
||||
if (existingContact) {
|
||||
// Update existing contact
|
||||
await this.$updateContact(contact.did, contact);
|
||||
// update the labels for the contact
|
||||
await this.$updateContactLabels(contact.did, contactLabels);
|
||||
await this.$updateContactLabels(contact.did, mergedLabels);
|
||||
updatedCount++;
|
||||
} else {
|
||||
// Add new contact
|
||||
await this.$insertContact(contact);
|
||||
// add the labels for the contact
|
||||
await this.$insertContactLabels(contact.did, contactLabels);
|
||||
await this.$insertContactLabels(contact.did, mergedLabels);
|
||||
importedCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let labelsAppliedToExistingCount = 0;
|
||||
// Apply selected labels to existing contacts with no changes if checkbox is checked
|
||||
if (this.applyLabelsToExisting && this.selectedLabels.length > 0) {
|
||||
for (let i = 0; i < this.contactsImporting.length; i++) {
|
||||
// Skip if this contact was already processed (selected)
|
||||
if (!this.contactsSelected[i]) {
|
||||
const contactWithLabels = this.contactsImporting[i];
|
||||
const existingContact = this.contactsExisting[contactWithLabels.did];
|
||||
|
||||
// Only process existing contacts (not selected means no changes)
|
||||
if (existingContact) {
|
||||
// Get current labels for this contact
|
||||
const currentLabels = await this.$getContactLabelsForDid(
|
||||
contactWithLabels.did,
|
||||
);
|
||||
// Merge current labels with selected labels
|
||||
const mergedLabels = Array.from(
|
||||
new Set([...currentLabels, ...this.selectedLabels]),
|
||||
);
|
||||
// Update labels if there are new ones to add
|
||||
if (mergedLabels.length > currentLabels.length) {
|
||||
await this.$updateContactLabels(
|
||||
contactWithLabels.did,
|
||||
mergedLabels,
|
||||
);
|
||||
labelsAppliedToExistingCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set visibility if requested
|
||||
if (this.makeVisible) {
|
||||
const failedVisibileToContacts = [];
|
||||
@@ -583,7 +783,10 @@ export default class ContactImportView extends Vue {
|
||||
// Show success notification
|
||||
this.notify.success(
|
||||
`${importedCount} contact${importedCount == 1 ? "" : "s"} imported.` +
|
||||
(updatedCount ? ` ${updatedCount} updated.` : ""),
|
||||
(updatedCount ? ` ${updatedCount} updated.` : "") +
|
||||
(labelsAppliedToExistingCount
|
||||
? ` ${labelsAppliedToExistingCount} labels applied to existing contacts.`
|
||||
: ""),
|
||||
TIMEOUTS.STANDARD,
|
||||
);
|
||||
this.$router.push({ name: "contacts" });
|
||||
|
||||
Reference in New Issue
Block a user