mirror of
https://github.com/inventree/InvenTree.git
synced 2025-04-29 03:56:43 +00:00
Link changes and in-table clipboards (#4697)
* Add clipboard to tables and external link changes * Clipboard icon added to tables for screens >1200px wide. Enables copying of SKU/MPN/IPN from table cells where these otherwise are hyperlinks * External links now open in new tabs with noreferrer * Move external links into separate template * All statically rendered external links have been moved out to a new template.
This commit is contained in:
parent
0b8feb2c4a
commit
6bd95f3b15
@ -1094,4 +1094,10 @@ a {
|
|||||||
.sso-provider-link a {
|
.sso-provider-link a {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.flex-cell {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
@ -106,7 +106,7 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<td><span class='fas fa-link'></span></td>
|
<td><span class='fas fa-link'></span></td>
|
||||||
<td>{% trans "External Link" %}</td>
|
<td>{% trans "External Link" %}</td>
|
||||||
<td><a href="{{ build.link }}">{{ build.link }}</a>{% include "clip.html"%}</td>
|
<td>{% include 'clip_link.html' with link=build.link %}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if build.issued_by %}
|
{% if build.issued_by %}
|
||||||
|
@ -105,7 +105,7 @@ src="{% static 'img/blank_image.png' %}"
|
|||||||
<tr>
|
<tr>
|
||||||
<td><span class='fas fa-link'></span></td>
|
<td><span class='fas fa-link'></span></td>
|
||||||
<td>{% trans "External Link" %}</td>
|
<td>{% trans "External Link" %}</td>
|
||||||
<td><a href="{{ part.link }}">{{ part.link }}</a>{% include "clip.html"%}</td>
|
<td>{% include 'clip_link.html' with link=part.link %}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</table>
|
</table>
|
||||||
|
@ -180,7 +180,7 @@ src="{% static 'img/blank_image.png' %}"
|
|||||||
<tr>
|
<tr>
|
||||||
<td><span class='fas fa-link'></span></td>
|
<td><span class='fas fa-link'></span></td>
|
||||||
<td>{% trans "External Link" %}</td>
|
<td>{% trans "External Link" %}</td>
|
||||||
<td><a href="{{ part.link }}">{{ part.link }}</a>{% include "clip.html"%}</td>
|
<td>{% include 'clip_link.html' with link=part.link %}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</table>
|
</table>
|
||||||
|
@ -168,8 +168,8 @@ src="{% static 'img/blank_image.png' %}"
|
|||||||
{% if order.link %}
|
{% if order.link %}
|
||||||
<tr>
|
<tr>
|
||||||
<td><span class='fas fa-link'></span></td>
|
<td><span class='fas fa-link'></span></td>
|
||||||
<td>External Link</td>
|
<td>{% trans "External Link" %}</td>
|
||||||
<td><a href="{{ order.link }}">{{ order.link }}</a>{% include "clip.html"%}</td>
|
<td>{% include 'clip_link.html' with link=order.link %}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<tr>
|
<tr>
|
||||||
|
@ -143,8 +143,8 @@ src="{% static 'img/blank_image.png' %}"
|
|||||||
{% if order.link %}
|
{% if order.link %}
|
||||||
<tr>
|
<tr>
|
||||||
<td><span class='fas fa-link'></span></td>
|
<td><span class='fas fa-link'></span></td>
|
||||||
<td>External Link</td>
|
<td>{% trans "External Link" %}</td>
|
||||||
<td><a href="{{ order.link }}">{{ order.link }}</a>{% include "clip.html"%}</td>
|
<td>{% include 'clip_link.html' with link=order.link %}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<tr>
|
<tr>
|
||||||
|
@ -178,8 +178,8 @@ src="{% static 'img/blank_image.png' %}"
|
|||||||
{% if order.link %}
|
{% if order.link %}
|
||||||
<tr>
|
<tr>
|
||||||
<td><span class='fas fa-link'></span></td>
|
<td><span class='fas fa-link'></span></td>
|
||||||
<td>External Link</td>
|
<td>{% trans "External Link" %}</td>
|
||||||
<td><a href="{{ order.link }}">{{ order.link }}</a>{% include "clip.html"%}</td>
|
<td>{% include 'clip_link.html' with link=order.link %}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<tr>
|
<tr>
|
||||||
|
@ -383,7 +383,7 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<td><span class='fas fa-link'></span></td>
|
<td><span class='fas fa-link'></span></td>
|
||||||
<td>{% trans "External Link" %}</td>
|
<td>{% trans "External Link" %}</td>
|
||||||
<td><a href="{{ part.link }}">{{ part.link }}</a>{% include "clip.html"%}</td>
|
<td>{% include 'clip_link.html' with link=part.link %}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if part.responsible %}
|
{% if part.responsible %}
|
||||||
|
@ -199,7 +199,7 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<td><span class='fas fa-link'></span>
|
<td><span class='fas fa-link'></span>
|
||||||
<td>{% trans "External Link" %}</td>
|
<td>{% trans "External Link" %}</td>
|
||||||
<td><a href="{{ item.link }}">{{ item.link }}</a></td>
|
<td>{% include 'clip_link.html' with link=item.link %}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if item.supplier_part.manufacturer_part %}
|
{% if item.supplier_part.manufacturer_part %}
|
||||||
|
5
InvenTree/templates/clip_link.html
Normal file
5
InvenTree/templates/clip_link.html
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
{% if link %}
|
||||||
|
<a href="{{ link }}">{{ link }}</a>{% include 'clip.html' %}
|
||||||
|
{% endif %}
|
@ -954,7 +954,7 @@ function loadManufacturerPartTable(table, url, options) {
|
|||||||
field: 'MPN',
|
field: 'MPN',
|
||||||
title: '{% trans "MPN" %}',
|
title: '{% trans "MPN" %}',
|
||||||
formatter: function(value, row) {
|
formatter: function(value, row) {
|
||||||
return renderLink(value, `/manufacturer-part/${row.pk}/`);
|
return renderClipboard(renderLink(value, `/manufacturer-part/${row.pk}/`));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -962,7 +962,7 @@ function loadManufacturerPartTable(table, url, options) {
|
|||||||
title: '{% trans "Link" %}',
|
title: '{% trans "Link" %}',
|
||||||
formatter: function(value) {
|
formatter: function(value) {
|
||||||
if (value) {
|
if (value) {
|
||||||
return renderLink(value, value);
|
return renderLink(value, value, {external: true});
|
||||||
} else {
|
} else {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
@ -1194,7 +1194,7 @@ function loadSupplierPartTable(table, url, options) {
|
|||||||
field: 'SKU',
|
field: 'SKU',
|
||||||
title: '{% trans "Supplier Part" %}',
|
title: '{% trans "Supplier Part" %}',
|
||||||
formatter: function(value, row) {
|
formatter: function(value, row) {
|
||||||
return renderLink(value, `/supplier-part/${row.pk}/`);
|
return renderClipboard(renderLink(value, `/supplier-part/${row.pk}/`));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -1225,7 +1225,7 @@ function loadSupplierPartTable(table, url, options) {
|
|||||||
title: '{% trans "MPN" %}',
|
title: '{% trans "MPN" %}',
|
||||||
formatter: function(value, row) {
|
formatter: function(value, row) {
|
||||||
if (value && row.manufacturer_part) {
|
if (value && row.manufacturer_part) {
|
||||||
return renderLink(value, `/manufacturer-part/${row.manufacturer_part}/`);
|
return renderClipboard(renderLink(value, `/manufacturer-part/${row.manufacturer_part}/`));
|
||||||
} else {
|
} else {
|
||||||
return '-';
|
return '-';
|
||||||
}
|
}
|
||||||
@ -1261,7 +1261,7 @@ function loadSupplierPartTable(table, url, options) {
|
|||||||
title: '{% trans "Link" %}',
|
title: '{% trans "Link" %}',
|
||||||
formatter: function(value) {
|
formatter: function(value) {
|
||||||
if (value) {
|
if (value) {
|
||||||
return renderLink(value, value);
|
return renderLink(value, value, {external: true});
|
||||||
} else {
|
} else {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
yesNoLabel,
|
yesNoLabel,
|
||||||
withTitle,
|
withTitle,
|
||||||
wrapButtons,
|
wrapButtons,
|
||||||
|
renderClipboard,
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* exported
|
/* exported
|
||||||
@ -376,7 +377,13 @@ function renderLink(text, url, options={}) {
|
|||||||
extras += ` download`;
|
extras += ` download`;
|
||||||
}
|
}
|
||||||
|
|
||||||
return `<a href="${url}" ${extras}>${text}</a>`;
|
let suffix = '';
|
||||||
|
if (options.external) {
|
||||||
|
extras += ` target="_blank" rel="noopener noreferrer"`;
|
||||||
|
|
||||||
|
suffix = ` <i class="fas fa-external-link-alt fa-xs d-none d-xl-inline"></i>`;
|
||||||
|
}
|
||||||
|
return `<a href="${url}" ${extras}>${text}${suffix}</a>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -516,3 +523,25 @@ function sanitizeInputString(s, options={}) {
|
|||||||
|
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Inserts HTML data equal to clip.html into input string
|
||||||
|
* Enables insertion of clipboard icons in dynamic tables
|
||||||
|
*
|
||||||
|
* clipString relies on ClipboardJS in the same manner as clip.html
|
||||||
|
* Thus, this functionality will break if the call to
|
||||||
|
* attachClipboard('.clip-btn') in script/inventree/inventree.js is altered
|
||||||
|
*/
|
||||||
|
function renderClipboard(s, prepend=false) {
|
||||||
|
if (!s || typeof s != 'string') {
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
let clipString = `<span class="d-none d-xl-inline"><button class="btn clip-btn" type="button" data-bs-toggle='tooltip' title='{% trans "copy to clipboard" %}'><em class="fas fa-copy"></em></button></span>`;
|
||||||
|
|
||||||
|
if (prepend === true) {
|
||||||
|
return `<div class="flex-cell">${clipString+s}</div>`;
|
||||||
|
} else {
|
||||||
|
return `<div class="flex-cell">${s+clipString}</div>`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1447,7 +1447,7 @@ function loadPartPurchaseOrderTable(table, part_id, options={}) {
|
|||||||
if (row.supplier_part_detail) {
|
if (row.supplier_part_detail) {
|
||||||
var supp = row.supplier_part_detail;
|
var supp = row.supplier_part_detail;
|
||||||
|
|
||||||
return renderLink(supp.SKU, `/supplier-part/${supp.pk}/`);
|
return renderClipboard(renderLink(supp.SKU, `/supplier-part/${supp.pk}/`));
|
||||||
} else {
|
} else {
|
||||||
return '-';
|
return '-';
|
||||||
}
|
}
|
||||||
@ -1460,7 +1460,7 @@ function loadPartPurchaseOrderTable(table, part_id, options={}) {
|
|||||||
formatter: function(value, row) {
|
formatter: function(value, row) {
|
||||||
if (row.supplier_part_detail && row.supplier_part_detail.manufacturer_part_detail) {
|
if (row.supplier_part_detail && row.supplier_part_detail.manufacturer_part_detail) {
|
||||||
var manu = row.supplier_part_detail.manufacturer_part_detail;
|
var manu = row.supplier_part_detail.manufacturer_part_detail;
|
||||||
return renderLink(manu.MPN, `/manufacturer-part/${manu.pk}/`);
|
return renderClipboard(renderLink(manu.MPN, `/manufacturer-part/${manu.pk}/`));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -1955,7 +1955,7 @@ function loadPurchaseOrderLineItemTable(table, options={}) {
|
|||||||
title: '{% trans "SKU" %}',
|
title: '{% trans "SKU" %}',
|
||||||
formatter: function(value, row, index, field) {
|
formatter: function(value, row, index, field) {
|
||||||
if (value) {
|
if (value) {
|
||||||
return renderLink(value, `/supplier-part/${row.part}/`);
|
return renderClipboard(renderLink(value, `/supplier-part/${row.part}/`));
|
||||||
} else {
|
} else {
|
||||||
return '-';
|
return '-';
|
||||||
}
|
}
|
||||||
@ -1967,7 +1967,7 @@ function loadPurchaseOrderLineItemTable(table, options={}) {
|
|||||||
title: '{% trans "Link" %}',
|
title: '{% trans "Link" %}',
|
||||||
formatter: function(value, row, index, field) {
|
formatter: function(value, row, index, field) {
|
||||||
if (value) {
|
if (value) {
|
||||||
return renderLink(value, value);
|
return renderLink(value, value, {external: true});
|
||||||
} else {
|
} else {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
@ -1980,7 +1980,7 @@ function loadPurchaseOrderLineItemTable(table, options={}) {
|
|||||||
title: '{% trans "MPN" %}',
|
title: '{% trans "MPN" %}',
|
||||||
formatter: function(value, row, index, field) {
|
formatter: function(value, row, index, field) {
|
||||||
if (row.supplier_part_detail && row.supplier_part_detail.manufacturer_part) {
|
if (row.supplier_part_detail && row.supplier_part_detail.manufacturer_part) {
|
||||||
return renderLink(value, `/manufacturer-part/${row.supplier_part_detail.manufacturer_part}/`);
|
return renderClipboard(renderLink(value, `/manufacturer-part/${row.supplier_part_detail.manufacturer_part}/`));
|
||||||
} else {
|
} else {
|
||||||
return '-';
|
return '-';
|
||||||
}
|
}
|
||||||
|
@ -1782,7 +1782,7 @@ function loadStockTable(table, options) {
|
|||||||
formatter: function(value, row) {
|
formatter: function(value, row) {
|
||||||
var ipn = row.part_detail.IPN;
|
var ipn = row.part_detail.IPN;
|
||||||
if (ipn) {
|
if (ipn) {
|
||||||
return withTitle(shortenString(ipn), ipn);
|
return renderClipboard(withTitle(shortenString(ipn), ipn));
|
||||||
} else {
|
} else {
|
||||||
return '-';
|
return '-';
|
||||||
}
|
}
|
||||||
@ -2014,7 +2014,7 @@ function loadStockTable(table, options) {
|
|||||||
text = `<i>{% trans "Supplier part not specified" %}</i>`;
|
text = `<i>{% trans "Supplier part not specified" %}</i>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
return renderLink(text, link);
|
return renderClipboard(renderLink(text, link));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user