Entregas gratuitas em Portugal Continental nas marcas exclusivas Marcolino 1926.

0
enpt

O seu carrinho

Ainda não tem nada no seu Carrinho

Entre ou crie uma conta para desbloquear recompensas exclusivas para membros e recomendações personalizadas.

Marcolino Link - Joalharia e Relojoaria Online

TISSOT

TISSOT

Comprar
% Saldos Pandora · até -51% Stock limitado — seleção de joias com desconto
Entregas GratisAcima de 39€
Entregas em 48hPortugal Continental
Click & CollectLevanta em loja
Pagamento SeguroVisa, MB, MBWay

As nossas sugestões

Relógios Mulher

Relógios Mulher

Comprar
Relógios Homem

Relógios Homem

Comprar
Jóias

Jóias

Comprar
Novidades todas as semanas Descobre as últimas coleções das tuas marcas favoritas

Novidades

Ver Todos

Tendência

Os mais vistos nos últimos 30 dias

Para Ela

Para Ele

Outlet | Até -50% Oportunidades imperdíveis em marcas premium

Outlet

Ver Todos

Best Sellers

Segue-nos

Acompanha as novidades e inspirações no nosso Instagram e Facebook

Fica a par de tudo

Recebe em primeira mão novidades e ofertas exclusivas.

* Entrega grátis válida para Portugal Continental e Espanha Continental, em encomendas de valor igual ou superior a 39€.
não são interpretadas aqui. $postalErrorEs = Yii::t('frontend/repairs', 'Código Postal de Espanha inválido (5 dígitos, ex: 28001)'); $postalErrorPt = Yii::t('frontend/repairs', 'Código Postal de Portugal inválido (formato 0000-000)'); $scriptJS = << { const activeStep = document.querySelector(".form-content-active"); const nextStep = document.querySelector(".form-content[data-step='" + idStep + "']"); const activeStepBullet = document.querySelector(".form-step.active"); const nextStepBullet = document.querySelector(".form-step[data-step='" + idStep + "']"); const elmResume = document.querySelector(".modal-form-resume"); if(activeStep && nextStep && activeStepBullet && idStep){ if (idStep == 3) { elmResume.classList.add("show"); } else { elmResume.classList.remove("show"); } activeStep.classList.remove("form-content-active"); activeStep.classList.add("d-none"); nextStep.classList.add("form-content-active"); nextStep.classList.remove("d-none"); activeStepBullet.classList.remove("active"); nextStepBullet.classList.add("active"); } } const validateFields = (idStep) => { let formData = {}; var isValid = true; switch (idStep) { case "2": let step1 = $('#step-1').serialize(); decodeURIComponent(step1).split('&').forEach(function (pair) { pair = pair.split('='); formData[pair[0]] = pair[1] || ''; }); let requiredFields = document.querySelectorAll('#step-1 [required]'); // trim antes do truthy check — espaço/whitespace já não passa // como válido, e validamos o formato do email para apanhar typos // antes de chegar ao servidor. const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; requiredFields.forEach(function (field) { let fieldName = field.name; let value = (formData[fieldName] || '').trim(); let invalid = !value; if (!invalid && fieldName === 'email' && !emailRegex.test(value)) { invalid = true; } if (invalid) { isValid = false; field.style.cssText = 'border:1px solid rgb(165, 0, 0)!important;'; } else { field.style.border = ''; } }); return isValid; break; case "3": if(validateFields("2")) { let checkboxes = document.querySelectorAll('.btn-checkbox'); let checkboxesChoices = document.querySelectorAll('.btn-checkbox-choice'); let typeChecked = Array.from(checkboxes).some(function (checkbox) { return checkbox.checked; }); let choicesChecked = Array.from(checkboxesChoices).some(function (checkbox) { return checkbox.checked; }); if(!typeChecked) { isValid = false; let errorSelection = document.querySelector(".error-selection"); if(errorSelection == null) { let warningMessage = document.createElement('div'); warningMessage.className = 'help-block help-block-error error-selection'; warningMessage.textContent = '$messageError'; warningMessage.style = 'color: #a50000;display:block;text-align:center;margin-bottom:10px'; let formButtonsSelectionDiv = document.querySelector(".form-buttons-selection"); formButtonsSelectionDiv.parentNode.insertBefore(warningMessage, formButtonsSelectionDiv); } } else { let errorSelection = document.querySelector(".error-selection"); if(errorSelection != null) { errorSelection.remove() } } if(!choicesChecked) { isValid = false; let errorOptions = document.querySelector(".error-options"); if(errorOptions == null) { let warningMessageChoice = document.createElement('div'); warningMessageChoice.className = 'help-block help-block-error error-options'; warningMessageChoice.textContent = '$messageError'; warningMessageChoice.style = 'color: #a50000;display:block;text-align:center;margin-bottom:10px'; let formButtonsSelectionDivChoice = document.querySelector(".form-checkbox-options"); formButtonsSelectionDivChoice.parentNode.insertBefore(warningMessageChoice, formButtonsSelectionDivChoice); } }else { let errorOptions = document.querySelector(".error-options"); if(errorOptions != null) { errorOptions.remove() } } return isValid; } else { return false; } break; default: return true; break; } } const elmNavNextButton = document.querySelectorAll(".next-step"); if(elmNavNextButton && elmNavNextButton.length > 0){ elmNavNextButton.forEach((node) =>{ node.addEventListener("click",(e)=>{ e.preventDefault(); let idStep = node.getAttribute("data-step"); if(idStep){ let isValid = validateFields(idStep); if(isValid) { handleIdNavigation(idStep); } else { toastr.error('Preencha todos os campos', 'Erro', { closeButton: true, progressBar: true, positionClass: "toast-top-right", timeOut: 5000 }); } } scrollToTop(); }) }); } const elmNavPrevButton = document.querySelectorAll(".back-step"); if(elmNavPrevButton && elmNavPrevButton.length > 0){ elmNavPrevButton.forEach((node) =>{ node.addEventListener("click",(e)=>{ e.preventDefault(); let idStep = node.getAttribute("data-step"); if(idStep){ handleIdNavigation(idStep); } scrollToTop(); }) }) } const elmNavBullets = document.querySelectorAll('.form-steps-wrapper .form-step'); if(elmNavBullets && elmNavBullets.length > 0){ elmNavBullets.forEach((node) =>{ node.addEventListener("click",(e)=>{ e.preventDefault(); let idStep = node.getAttribute("data-step"); if(idStep){ let isValid = validateFields(idStep); if(isValid) { handleIdNavigation(idStep); } } scrollToTop(); }) }) } /* === UPLOAD FILE === */ const elmWarrantyCheck = document.querySelectorAll(".warranty-upload"); //console.log(elmWarrantyCheck) if(elmWarrantyCheck){ elmWarrantyCheck.forEach((node)=>{ node.addEventListener("click", (e)=>{ node.closest(".file-upload-wrapper").classList.toggle("active"); }) }) } const elmWarrantyButtonUpload = document.querySelector(".open-warranty-file"); const elmWarrantyFileUpload = document.querySelector(".warranty-file"); if(elmWarrantyButtonUpload && elmWarrantyFileUpload){ elmWarrantyButtonUpload.addEventListener("click",()=>{ elmWarrantyFileUpload.click(); }) } const btnProblem = document.querySelectorAll(".btn-problem"); if (btnProblem && btnProblem.length > 0) { btnProblem.forEach((node) => { node.addEventListener("click", (e) => { e.preventDefault(); btnProblem.forEach((btn) => { if (btn !== node) { const btnCheckbox = btn.querySelector(".btn-checkbox"); if (btnCheckbox) { btnCheckbox.removeAttribute("checked"); } btn.classList.remove("btn-selected"); btn.classList.remove("grey"); btn.classList.add("grey-clear"); } }); const btnCheckbox = node.querySelector(".btn-checkbox"); if (btnCheckbox) { btnCheckbox.setAttribute("checked", "true"); } node.classList.add("btn-selected"); node.classList.remove("grey-clear"); node.classList.add("grey"); }); }); } const scrollToTop = () => { const modal = document.querySelector('.modal-wrapper.repairs-modal'); if (modal) { modal.scrollTo({ top: 0, behavior: 'smooth' }); } }; $('input[name="delivery_option"]').on('change', function() { if ($(this).val() === 'store') { $("#store").removeClass("d-none"); $("#home").addClass("d-none"); $("#pickup_date").prop('required', true); $("#pickup_time_start").prop('required', true); $("#pickup_time_end").prop('required', true); $("#pickup_date").val(''); $("#pickup_time_start").val(''); $("#pickup_time_end").val(''); $('#final-step').removeClass('d-none'); // Limpar os campos da recolha, mas repor o país no default (PT) — // um val('') no select deixava-o sem seleção e o CP espanhol passava // a ser validado como português. $("#home input, #home textarea").val(''); $("#home select").not('#pickup_country').val(''); $('#pickup_country').val('PT'); } else { $("#store").addClass("d-none"); $("#home").removeClass("d-none"); $("#pickup_date").prop('required', false); $("#pickup_time_start").prop('required', false); $("#pickup_time_end").prop('required', false); $('#final-step').addClass('d-none'); $('.pickup-form').removeClass('d-none'); } }); $('#request-pickup').on('click', function() { var requiredFields = ['pickup_name', 'pickup_address', 'pickup_city', 'pickup_postal_code', 'pickup_country', 'pickup_phone', 'pickup_email', 'pickup_date', 'pickup_time_start', 'pickup_time_end', 'watch_front', 'watch_back']; var isValid = true; requiredFields.forEach(function(field) { var fieldInput = $('[name="' + field + '"]'); var value = fieldInput.val(); if (fieldInput.attr('type') === 'file') { if (!fieldInput[0].files.length) { isValid = false; fieldInput.css('border', '1px solid rgb(165, 0, 0)'); } else { fieldInput.css('border', ''); } } else { if (!value || !value.trim()) { isValid = false; fieldInput.css('border', '1px solid rgb(165, 0, 0)'); } else { fieldInput.css('border', ''); } } }); // Formato do código postal por país (PT 0000-000, ES 5 dígitos) — validar // aqui evita que o erro só apareça do servidor com o formulário já escondido. var pickupCountryVal = $('#pickup_country').val() || 'PT'; var postalRaw = ($('#pickup_postal_code').val() || '').trim(); var postalDigits = postalRaw.replace(/\D/g, ''); var postalOk = pickupCountryVal === 'ES' ? /^\d{5}$/.test(postalDigits) : /^\d{7}$/.test(postalDigits); if (isValid && !postalOk) { isValid = false; $('#pickup_postal_code').css('border', '1px solid rgb(165, 0, 0)'); toastr.error( pickupCountryVal === 'ES' ? '{$postalErrorEs}' : '{$postalErrorPt}', 'Erro', { closeButton: true, progressBar: true, positionClass: "toast-top-right" } ); return; } if (!isValid) { toastr.error('Preencha todos os campos', 'Erro', { closeButton: true, progressBar: true, positionClass: "toast-top-right", timeOut: 5000 }); return; } var pickupDetails = { name: $('[name="pickup_name"]').val(), address: $('[name="pickup_address"]').val(), city: $('[name="pickup_city"]').val(), postal: $('[name="pickup_postal_code"]').val(), phone: $('[name="pickup_phone"]').val(), email: $('[name="pickup_email"]').val(), date: $('[name="pickup_date"]').val(), timeStart: $('[name="pickup_time_start"]').val(), timeEnd: $('[name="pickup_time_end"]').val() }; $('#summary-name').text(pickupDetails.name); $('#summary-address').text(pickupDetails.address); $('#summary-city').text(pickupDetails.city); $('#summary-postal').text(pickupDetails.postal); $('#summary-country').text($('#pickup_country option:selected').text()); $('#summary-phone').text(pickupDetails.phone); $('#summary-email').text(pickupDetails.email); $('#summary-date').text(new Date(pickupDetails.date).toLocaleDateString()); $('#summary-time').text(pickupDetails.timeStart + ' - ' + pickupDetails.timeEnd); $('.pickup-form').addClass('d-none'); $('#pickup-success').removeClass('d-none'); $('#final-step').removeClass('d-none'); $('.form-steps-info').addClass('d-none'); }); $('#pickup_country').on('change', function() { $('#pickup_postal_code').attr('placeholder', $(this).val() === 'ES' ? '28001' : '0000-000'); }); $('#pickup_time_start, #pickup_time_end').on('change', function() { const startTime = $('#pickup_time_start').val(); const endTime = $('#pickup_time_end').val(); if (startTime && endTime && startTime >= endTime) { toastr.error('A hora final deve ser posterior à hora inicial', 'Erro', { closeButton: true, progressBar: true, positionClass: "toast-top-right", timeOut: 5000 }); $('#pickup_time_end').val(''); } // Validate minimum time difference (2 hours) if (startTime && endTime) { // Convert times to minutes for comparison const [startHour, startMinute] = startTime.split(':').map(Number); const [endHour, endMinute] = endTime.split(':').map(Number); const startTotalMinutes = startHour * 60 + startMinute; const endTotalMinutes = endHour * 60 + endMinute; // Check if difference is less than 2 hours (120 minutes) if (endTotalMinutes - startTotalMinutes < 120) { toastr.error('O período de recolha deve ser de pelo menos 2 horas', 'Erro', { closeButton: true, progressBar: true, positionClass: "toast-top-right", timeOut: 5000 }); $('#pickup_time_end').val(''); } } }); JS; $this->registerJS($scriptJS); ?>