f.name));
if (this.uploadedFiles.length + files.length > 5) {
alert('You can only upload up to 5 branding files.');
return;
}
files.forEach(file => {
const ext = file.name.split('.').pop().toLowerCase();
if (!['pdf', 'ai', 'eps', 'jpg', 'jpeg', 'png'].includes(ext)) {
alert('Only PDF, AI, EPS, JPG, and PNG files are accepted.');
return;
}
if (file.size > 10 * 1024 * 1024) {
alert('File size exceeds the 10MB limit.');
return;
}
this.uploadFile(file);
});
},
updateFileState(filename, data) {
const fileObj = this.uploadedFiles.find(f => f.name === filename);
if (fileObj) {
Object.assign(fileObj, data);
this.uploadedFiles = [...this.uploadedFiles]; // Force Alpine reactivity
}
},
uploadFile(file) {
this.uploadedFiles.push({
name: file.name,
progress: 0,
hashed_name: null,
error: null,
status: 'uploading'
});
this.isUploading = true;
this.uploadedFiles = [...this.uploadedFiles]; // Force immediate display of progress bar
console.log('uploadFile: Preparing payload for:', file.name);
const formData = new FormData();
formData.append('file', file);
const csrfToken = document.querySelector('meta[name=csrf-token]')?.getAttribute('content') || '';
console.log('uploadFile: CSRF token lookup result:', csrfToken ? 'Token Found' : 'Token Missing!');
const xhr = new XMLHttpRequest();
xhr.open('POST', '/upload-quote-file');
xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
if (csrfToken) {
xhr.setRequestHeader('X-CSRF-TOKEN', csrfToken);
}
xhr.upload.addEventListener('progress', e => {
if (e.lengthComputable) {
const percentage = Math.round((e.loaded / e.total) * 100);
console.log('uploadFile progress (' + file.name + '): ' + percentage + '%');
this.updateFileState(file.name, { progress: percentage });
}
});
xhr.addEventListener('load', () => {
console.log('uploadFile load complete (' + file.name + '). Status code:', xhr.status);
if (xhr.status >= 200 && xhr.status < 300) {
try {
const response = JSON.parse(xhr.responseText);
console.log('uploadFile response (' + file.name + '):', response);
if (response.success) {
this.updateFileState(file.name, {
hashed_name: response.hashed_name,
status: 'success',
progress: 100
});
} else {
this.updateFileState(file.name, {
error: response.message || 'Upload failed.',
status: 'error'
});
}
} catch(err) {
console.error('JSON parse exception during load handler:', err);
this.updateFileState(file.name, {
error: 'Invalid server response.',
status: 'error'
});
}
} else {
let msg = 'Upload failed.';
try {
const err = JSON.parse(xhr.responseText);
msg = err.message || msg;
} catch(e) {}
this.updateFileState(file.name, {
error: msg,
status: 'error'
});
}
this.checkUploadStatus();
});
xhr.addEventListener('error', () => {
console.error('uploadFile net error (' + file.name + ')');
this.updateFileState(file.name, {
error: 'Network error occurred.',
status: 'error'
});
this.checkUploadStatus();
});
xhr.send(formData);
console.log('uploadFile: Sent payload for:', file.name);
},
checkUploadStatus() {
this.isUploading = this.uploadedFiles.some(f => f.status === 'uploading');
},
removeUploadedFile(index) {
this.uploadedFiles.splice(index, 1);
this.checkUploadStatus();
},
getBrandingFilesPayload() {
return this.uploadedFiles
.filter(f => f.status === 'success')
.map(f => ({
original_name: f.name,
hashed_name: f.hashed_name
}));
},
getAddIsDisabled(id) {
const cartElement = document.querySelector('[x-data=\'shoppingCart\']');
const cart = Alpine.$data(cartElement);
return cart.isInCart(id, this.getColourTitle());
},
init() {
// Clean up any leading '?&' from the search query string and parse parameters
let search = window.location.search;
search = search.replace(/^\?&/, '?');
const params = new URLSearchParams(search);
// Automatically set the Quantity
const quantityParam = params.get('quantity') || params.get('qty');
if (quantityParam) {
const qty = parseInt(quantityParam, 10);
if (!isNaN(qty) && qty > 0) {
this._quantity = qty;
}
}
// Automatically select the Color
const colourParam = params.get('colour') || params.get('color');
if (colourParam) {
this.$nextTick(() => {
const decodedColour = decodeURIComponent(colourParam).trim().toLowerCase();
const buttons = Array.from(document.querySelectorAll('.product-details button[title]'));
const match = buttons.find(b => b.getAttribute('title').trim().toLowerCase() === decodedColour);
if (match) match.click(); // Triggers the Alpine click handler to select color and update corresponding image
});
}
}
}">
Home
/
Catalogue
/
Health & Personal
/
Temporary Tattoos
/
Temporary Tattoo Foil - 51mm x 76mm
Trends NZ
Temporary Tattoo Foil - 51mm x 76mm
Branding Logo or Images
Select Files
Accepted file types: PDF, AI, EPS, JPG, PNG | Max file size: 10MB | Max files: 1
check_circle
error
cancel
cancel Remove from Quote Cart
Uploading branding...
add_shopping_cart Add to Quote Cart
Product Description
expand_more
Vibrant 51mm x 76mm shiny metallic foil temporary tattoo which is ideal for interactive promotions, awareness campaigns and fundraising. It is printed with people friendly FDA CE certified non-toxic metallic ink and is a safe, fun product. A temporary tattoo is applied using water and easily removed with alcohol or baby oil. The quoted measurements are the paper size and not the image size. Any size is available on request and a production lead time of 35 working days applies to this product.
Features:
Temporary foil tattoos ideal for interactive promotions and awareness campaigns
FDA and CE certified non-toxic ink
Any size is available on request
Production lead time of 35 working days
Dimensions
L 76 x W 51
Packaging
Loose packed.
Branding Options
Digital Print: 47mm x 72mm.
Finish
Shiny Metallic
Production Lead Time
35 working days by air freight
Ordering Limitations
Quoted measurements are paper size not image size.
Country of Origin
United States
More in Health & Personal