Implement contact matching
This commit is contained in:
129
core/templates/pages/compose-contact-match.html
Normal file
129
core/templates/pages/compose-contact-match.html
Normal file
@@ -0,0 +1,129 @@
|
||||
{% extends "index.html" %}
|
||||
|
||||
{% block content %}
|
||||
<section class="section">
|
||||
<div class="container">
|
||||
<div class="level" style="margin-bottom: 0.75rem;">
|
||||
<div class="level-left">
|
||||
<div>
|
||||
<h1 class="title is-4" style="margin-bottom: 0.2rem;">Contact Match</h1>
|
||||
<p class="is-size-7 has-text-grey">
|
||||
Manually link Signal, WhatsApp, Instagram, and XMPP identifiers to people.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="level-right">
|
||||
<a class="button is-light" href="{% url 'compose_workspace' %}">
|
||||
<span class="icon is-small"><i class="fa-solid fa-table-cells-large"></i></span>
|
||||
<span>Manual Workspace</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if notice_message %}
|
||||
<article class="notification is-{{ notice_level|default:'info' }} is-light">
|
||||
{{ notice_message }}
|
||||
</article>
|
||||
{% endif %}
|
||||
|
||||
<div class="columns is-variable is-4">
|
||||
<div class="column is-5">
|
||||
<article class="box">
|
||||
<h2 class="title is-6">Create Or Link Identifier</h2>
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
<div class="field">
|
||||
<label class="label is-small">Service</label>
|
||||
<div class="select is-fullwidth">
|
||||
<select name="service" required>
|
||||
{% for key, label in service_choices %}
|
||||
<option value="{{ key }}" {% if key == prefill_service %}selected{% endif %}>
|
||||
{{ label }}
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label is-small">Identifier</label>
|
||||
<div class="control">
|
||||
<input class="input" type="text" name="identifier" value="{{ prefill_identifier }}" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label is-small">Existing Person</label>
|
||||
<div class="select is-fullwidth">
|
||||
<select name="person_id">
|
||||
<option value="">- Select person -</option>
|
||||
{% for person in people %}
|
||||
<option value="{{ person.id }}">{{ person.name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label is-small">Or Create Person</label>
|
||||
<div class="control">
|
||||
<input class="input" type="text" name="person_name" placeholder="New person name">
|
||||
</div>
|
||||
</div>
|
||||
<button class="button is-link" type="submit">
|
||||
<span class="icon is-small"><i class="fa-solid fa-link"></i></span>
|
||||
<span>Save Match</span>
|
||||
</button>
|
||||
</form>
|
||||
</article>
|
||||
</div>
|
||||
|
||||
<div class="column is-7">
|
||||
<article class="box">
|
||||
<h2 class="title is-6">Discovered Contacts</h2>
|
||||
{% if candidates %}
|
||||
<div class="table-container">
|
||||
<table class="table is-fullwidth is-hoverable is-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Contact</th>
|
||||
<th>Service</th>
|
||||
<th>Identifier</th>
|
||||
<th>Status</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for row in candidates %}
|
||||
<tr>
|
||||
<td>{{ row.person_name }}</td>
|
||||
<td>
|
||||
<span class="icon is-small"><i class="{{ row.service_icon_class }}"></i></span>
|
||||
{{ row.service|title }}
|
||||
</td>
|
||||
<td><code>{{ row.identifier }}</code></td>
|
||||
<td>
|
||||
{% if row.linked_person %}
|
||||
<span class="tag is-success is-light">linked</span>
|
||||
{% else %}
|
||||
<span class="tag is-warning is-light">unlinked</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
<a class="button is-small is-light" href="{{ row.compose_url }}">
|
||||
<span class="icon is-small"><i class="fa-solid fa-paper-plane"></i></span>
|
||||
<span>Message</span>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% else %}
|
||||
<p class="has-text-grey">No contacts discovered yet.</p>
|
||||
{% endif %}
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{% endblock %}
|
||||
@@ -64,7 +64,7 @@
|
||||
<span
|
||||
class="tag is-dark"
|
||||
style="min-width: 2.5rem; justify-content: center;">
|
||||
<i class="{{ manual_icon_class }}" aria-hidden="true"></i>
|
||||
<i class="{{ row.service_icon_class|default:manual_icon_class }}" aria-hidden="true"></i>
|
||||
</span>
|
||||
<span
|
||||
class="tag is-white"
|
||||
@@ -103,6 +103,15 @@
|
||||
</span>
|
||||
</span>
|
||||
</button>
|
||||
{% if not row.linked_person %}
|
||||
<a
|
||||
class="button is-small is-light"
|
||||
href="{{ row.match_url }}"
|
||||
title="Link this identifier to a person">
|
||||
<span class="icon is-small"><i class="fa-solid fa-link"></i></span>
|
||||
<span>Match</span>
|
||||
</a>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% else %}
|
||||
|
||||
@@ -1,15 +1,23 @@
|
||||
{% if items %}
|
||||
{% for item in items %}
|
||||
<a class="navbar-item" href="{{ item.compose_url }}">
|
||||
<span class="icon is-small"><i class="{{ manual_icon_class }}"></i></span>
|
||||
<span class="icon is-small"><i class="{{ item.service_icon_class|default:manual_icon_class }}"></i></span>
|
||||
<span style="margin-left: 0.35rem;">
|
||||
{{ item.person_name }} · {{ item.service|title }}
|
||||
{% if not item.linked_person %}
|
||||
<small class="has-text-grey"> · unlinked</small>
|
||||
{% endif %}
|
||||
</span>
|
||||
</a>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<a class="navbar-item is-disabled">No contacts found.</a>
|
||||
{% endif %}
|
||||
<hr class="navbar-divider" style="margin: 0.2rem 0;">
|
||||
<a class="navbar-item" href="{{ match_url }}">
|
||||
<span class="icon is-small"><i class="fa-solid fa-link"></i></span>
|
||||
<span style="margin-left: 0.35rem;">Match Contacts</span>
|
||||
</a>
|
||||
{% if is_preview %}
|
||||
<hr class="navbar-divider" style="margin: 0.2rem 0;">
|
||||
<a
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
</button>
|
||||
{% if show_contact_actions %}
|
||||
{% if type == 'page' %}
|
||||
<a href="{% url 'signal_contacts' type=type pk=item %}"><button
|
||||
<a href="{% url contacts_url_name type=type pk=item %}"><button
|
||||
class="button">
|
||||
<span class="icon-text">
|
||||
<span class="icon">
|
||||
@@ -47,7 +47,7 @@
|
||||
</span>
|
||||
</button>
|
||||
</a>
|
||||
<a href="{% url 'signal_chats' type=type pk=item %}"><button
|
||||
<a href="{% url chats_url_name type=type pk=item %}"><button
|
||||
class="button">
|
||||
<span class="icon-text">
|
||||
<span class="icon">
|
||||
@@ -59,7 +59,7 @@
|
||||
{% else %}
|
||||
<button
|
||||
hx-headers='{"X-CSRFToken": "{{ csrf_token }}"}'
|
||||
hx-get="{% url 'signal_contacts' type=type pk=item %}"
|
||||
hx-get="{% url contacts_url_name type=type pk=item %}"
|
||||
hx-trigger="click"
|
||||
hx-target="#{{ type }}s-here"
|
||||
hx-swap="innerHTML"
|
||||
@@ -72,7 +72,7 @@
|
||||
</button>
|
||||
<button
|
||||
hx-headers='{"X-CSRFToken": "{{ csrf_token }}"}'
|
||||
hx-get="{% url 'signal_chats' type=type pk=item %}"
|
||||
hx-get="{% url chats_url_name type=type pk=item %}"
|
||||
hx-trigger="click"
|
||||
hx-target="#{{ type }}s-here"
|
||||
hx-swap="innerHTML"
|
||||
|
||||
@@ -68,6 +68,16 @@
|
||||
</span>
|
||||
</button>
|
||||
{% endif %}
|
||||
<a href="{{ item.match_url }}"><button
|
||||
class="button"
|
||||
title="Match identifier to person">
|
||||
<span class="icon-text">
|
||||
<span class="icon">
|
||||
<i class="fa-solid fa-link"></i>
|
||||
</span>
|
||||
</span>
|
||||
</button>
|
||||
</a>
|
||||
<a href="{{ item.ai_url }}"><button
|
||||
class="button"
|
||||
title="Open AI workspace">
|
||||
@@ -102,6 +112,13 @@
|
||||
</span>
|
||||
</button>
|
||||
{% endif %}
|
||||
<a href="{{ item.match_url }}"><button class="button" title="Match identifier to person">
|
||||
<span class="icon-text">
|
||||
<span class="icon">
|
||||
<i class="fa-solid fa-link"></i>
|
||||
</span>
|
||||
</span>
|
||||
</button></a>
|
||||
<a href="{{ item.ai_url }}"><button class="button">
|
||||
<span class="icon-text">
|
||||
<span class="icon">
|
||||
|
||||
57
core/templates/partials/whatsapp-chats-list.html
Normal file
57
core/templates/partials/whatsapp-chats-list.html
Normal file
@@ -0,0 +1,57 @@
|
||||
{% include 'mixins/partials/notify.html' %}
|
||||
<table
|
||||
class="table is-fullwidth is-hoverable"
|
||||
hx-target="#{{ context_object_name }}-table"
|
||||
id="{{ context_object_name }}-table"
|
||||
hx-swap="outerHTML"
|
||||
hx-trigger="{{ context_object_name_singular }}Event from:body"
|
||||
hx-get="{{ list_url }}">
|
||||
<thead>
|
||||
<th>chat</th>
|
||||
<th>identifier</th>
|
||||
<th>person</th>
|
||||
<th>actions</th>
|
||||
</thead>
|
||||
{% for item in object_list %}
|
||||
<tr>
|
||||
<td>{{ item.name|default:"WhatsApp Chat" }}</td>
|
||||
<td>
|
||||
<a
|
||||
class="has-text-grey button nowrap-child"
|
||||
onclick="window.prompt('Copy to clipboard: Ctrl+C, Enter', '{{ item.identifier }}');">
|
||||
<span class="icon" data-tooltip="Copy identifier">
|
||||
<i class="fa-solid fa-copy" aria-hidden="true"></i>
|
||||
</span>
|
||||
</a>
|
||||
</td>
|
||||
<td>{{ item.person_name|default:"-" }}</td>
|
||||
<td>
|
||||
<div class="buttons">
|
||||
{% if type == 'page' %}
|
||||
<a href="{{ item.compose_page_url }}" class="button" title="Manual text mode">
|
||||
<span class="icon"><i class="fa-solid fa-paper-plane"></i></span>
|
||||
</a>
|
||||
{% else %}
|
||||
<button
|
||||
hx-headers='{"X-CSRFToken": "{{ csrf_token }}"}'
|
||||
hx-get="{{ item.compose_widget_url }}"
|
||||
hx-trigger="click"
|
||||
hx-target="#widgets-here"
|
||||
hx-swap="afterend"
|
||||
class="button"
|
||||
title="Manual text mode widget">
|
||||
<span class="icon"><i class="fa-solid fa-paper-plane"></i></span>
|
||||
</button>
|
||||
{% endif %}
|
||||
<a href="{{ item.match_url }}" class="button" title="Match identifier">
|
||||
<span class="icon"><i class="fa-solid fa-link"></i></span>
|
||||
</a>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{% empty %}
|
||||
<tr>
|
||||
<td colspan="4" class="has-text-grey">No WhatsApp chats discovered yet.</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
53
core/templates/partials/whatsapp-contacts-list.html
Normal file
53
core/templates/partials/whatsapp-contacts-list.html
Normal file
@@ -0,0 +1,53 @@
|
||||
{% include 'mixins/partials/notify.html' %}
|
||||
<table
|
||||
class="table is-fullwidth is-hoverable"
|
||||
hx-target="#{{ context_object_name }}-table"
|
||||
id="{{ context_object_name }}-table"
|
||||
hx-swap="outerHTML"
|
||||
hx-trigger="{{ context_object_name_singular }}Event from:body"
|
||||
hx-get="{{ list_url }}">
|
||||
<thead>
|
||||
<th>name</th>
|
||||
<th>identifier</th>
|
||||
<th>jid</th>
|
||||
<th>person</th>
|
||||
<th>actions</th>
|
||||
</thead>
|
||||
{% for item in object_list %}
|
||||
<tr>
|
||||
<td>{{ item.name|default:"-" }}</td>
|
||||
<td>
|
||||
<code>{{ item.identifier }}</code>
|
||||
</td>
|
||||
<td>{{ item.jid|default:"-" }}</td>
|
||||
<td>{{ item.person_name|default:"-" }}</td>
|
||||
<td>
|
||||
<div class="buttons">
|
||||
{% if type == 'page' %}
|
||||
<a href="{{ item.compose_page_url }}" class="button" title="Open manual chat">
|
||||
<span class="icon"><i class="fa-solid fa-paper-plane"></i></span>
|
||||
</a>
|
||||
{% else %}
|
||||
<button
|
||||
hx-headers='{"X-CSRFToken": "{{ csrf_token }}"}'
|
||||
hx-get="{{ item.compose_widget_url }}"
|
||||
hx-trigger="click"
|
||||
hx-target="#widgets-here"
|
||||
hx-swap="afterend"
|
||||
class="button"
|
||||
title="Open manual chat widget">
|
||||
<span class="icon"><i class="fa-solid fa-paper-plane"></i></span>
|
||||
</button>
|
||||
{% endif %}
|
||||
<a href="{{ item.match_url }}" class="button" title="Match identifier">
|
||||
<span class="icon"><i class="fa-solid fa-link"></i></span>
|
||||
</a>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{% empty %}
|
||||
<tr>
|
||||
<td colspan="5" class="has-text-grey">No WhatsApp contacts discovered yet.</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
Reference in New Issue
Block a user