Compare commits

..

4 Commits

Author SHA1 Message Date
Remo Zaros
0ba7c07287 rewrite API call 2026-06-10 09:11:28 +02:00
Remo Zaros
558ea24cec style postcode form 2026-06-01 12:03:17 +02:00
Remo Zaros
f0b7c405fa add extra error messages 2026-06-01 12:02:59 +02:00
Remo Zaros
a9c7f7c70f increase version 2026-06-01 12:01:38 +02:00
3 changed files with 255 additions and 122 deletions

View File

@@ -15,6 +15,9 @@
transition:
opacity 0.3s ease,
display 0.3s allow-discrete;
@starting-style {
opacity: 0;
}
}
h2 {
@@ -40,6 +43,7 @@
caret-color: var(--wp--preset--color--vivid-red);
background: hsl(0 1 80);
border: none;
text-transform: uppercase;
&:focus,
&:valid,
@@ -50,14 +54,17 @@
}
}
button {
#postcode_modal_submit {
display: grid;
grid-template-columns: 1fr;
grid-template-rows: 1fr;
place-items: center;
font-size: 1.2rem;
font-weight: 700;
color: hsl(233 100 100);
padding: 0.4rem 1rem;
background: var(--wp--preset--color--vivid-red);
border: 1px var(--wp--preset--color--vivid-red) solid;
margin: 0 0 0 0.3rem;
transition: 0.2s all linear;
padding: 0.2em 2.5em;
&:hover {
@@ -65,16 +72,23 @@
background: hsl(344 98 100);
cursor: pointer;
}
}
&:hover > .btn_loader > svg > circle {
fill: var(--wp--preset--color--vivid-red);
}
.btn_loader,
.btn_text {
transition: 0.2s opacity linear;
grid-row: 1 / -1;
grid-column: 1 / -1;
}
}
.form_fields {
display: flex;
justify-content: space-between;
justify-content: center;
align-items: center;
div {
display: flex;
gap: 1.5rem;
}
gap: 0.5rem;
}
&::backdrop {
@@ -87,3 +101,27 @@
color: var(--wp--preset--color--vivid-red);
} /* HTML: <div class="loader"></div> */
}
/* HTML: <div class="loader"></div> */
.btn_loader {
opacity: 0;
svg {
height: 0.4rem;
}
circle {
fill: white;
transition: 0.2s fill linear;
animation: loader-pulse 1.5s ease-in-out infinite;
animation-delay: calc((var(--i, 0)) * 0.2s);
}
}
@keyframes loader-pulse {
0%,
100% {
opacity: 0.3; /* Dim state */
}
50% {
opacity: 1; /* Bright state */
}
}

View File

@@ -3,10 +3,10 @@
require_once plugin_dir_path(__FILE__) . "session_dialog.php";
require_once plugin_dir_path(__FILE__) . "admin.php";
/*
* Plugin Name: prijzen per poscode range
* Description: posctcodes in de 5000-5800 range krijgen een lokaal tarief aangeboden.
* Plugin Name: Prijzen per poscodeereeks.
* Description: Producten worden gefiltered aan de hand van opgegegeven postcodereksen.Klanten met een postcode Die in de opgegegeven postcode reeks vallen zullen een lokale prijs zien. Waar iedereen die niet in de reeks vallen de overige regios prijs te zien krijgt.
* Author: Remo Zaros
* Version: 0.9.5
* Version: 0.9.6
* Text Domeain: prijs-per-postcode
*/

View File

@@ -24,6 +24,8 @@ function modal_styles()
wp_enqueue_style(
"prijs-per-postcode",
plugins_url("/assets/postcode_modal.css", __FILE__),
[], // Voeg een lege dependencies array toe (verplicht)
filemtime(plugin_dir_path(__FILE__) . "assets/postcode_modal.css"), // Voeg versie toe
);
}
@@ -43,57 +45,97 @@ function send_postcode_data()
<script type="module">
const postcodeModal = document.querySelector("#postcode_modal");
const modalForm = document.querySelector("#postcode_modal_form");
const sndBtn = document.querySelector("#postcode_modal_submit");
const sndBtnLdr = sndBtn.querySelector(".btn_loader");
const sndBtnTxt = sndBtn.querySelector(".btn_text");
const errorMsg = document.querySelector("#error_message_modal_postcode");
// 1. Validatie bij blur (client-side checks)
modalForm.querySelectorAll("input").forEach(input => {
input.addEventListener("blur", ev => {
errorMsg.innerHTML = "";
if (!ev.target.validity.valid && ev.target.value !== "") {
errorMsg.innerHTML = ev.target.title;
}
});
});
// 2. Submit Handler
modalForm.addEventListener('submit', async (e) => {
e.preventDefault();
errorMsg.innerHTML = "";
// UI: Laadstatus
sndBtn.disabled = true;
sndBtnLdr.style.opacity = "1";
sndBtnTxt.style.opacity = "0";
const formData = new FormData(e.target);
const data = Object.fromEntries(formData.entries());
const json = JSON.stringify(data);
const payload = Object.fromEntries(formData.entries());
try {
const resp = await fetch('<?php echo get_rest_url(
null,
"postcode-modal/v1/submit",
); ?>', {
method: 'POST',
credentials: 'same-origin',
headers: {
'Content-Type': 'application/json',
'X-WP-Nonce': '<?php echo wp_create_nonce("wp_rest"); ?>'
},
body: json
});
const resp = await fetch('<?php echo get_rest_url(
null,
"postcode-modal/v1/submit",
); ?>', {
method: 'POST',
credentials: 'same-origin',
headers: {
'Content-Type': 'application/json',
'X-WP-Nonce': '<?php echo wp_create_nonce(
"wp_rest",
); ?>'
},
body: JSON.stringify(payload)
});
if(!resp.ok){
throw new Error(`HTTP Error! status: ${resp.status}` );
}
const data = await resp.json();
console.log("Data returnd", data);
// Parse altijd eerst de JSON, zelfs bij HTTP fouten
const data = await resp.json();
if (data.status === "error"){
const err = data.code;
let errmsg
// UI: Reset knop
sndBtn.disabled = false;
sndBtnLdr.style.opacity = "0";
sndBtnTxt.style.opacity = "1";
switch (err) {
case "HUISNUMMER_NOT_FOUND":
errmsg = "Adres niet gevonden.";
break;
case "HUISNUMMER_AMBIGUOUS":
errmsg = "Huisnummertovoeging mist.";
break;
default:
errmsg = "Gegevens niet correct.";
}
document.querySelector("#error_message_modal_postcode").innerHTML = errmsg;
}
if (data.status === "error") {
const errorCode = data.code; // Komt nu correct door vanuit PHP
const apiMessage = data.message;
if (data.status === "success"){
postcodeModal.close();
//location.reload();
}
}catch(err){
console.error("Fetch Failed:", err);
throw err;
let userErrorMessage = "Gegevens zijn niet correct.";
// Specifieke foutafhandeling
if (errorCode === "HUISNUMMER_NOT_FOUND" || apiMessage.includes("not found")) {
userErrorMessage = "Adres is niet gevonden.";
}
else if (errorCode === "HUISNUMMER_AMBIGUOUS") {
// Deze komt ALLEEN nog hier als PHP géén 'platte' match kon vinden.
// Dat betekent dat de gebruiker verplicht een toevoeging moet geven (bijv. alleen 15A bestaat).
const suggestions = data.suggestions || [];
if (suggestions.length > 0) {
userErrorMessage = `Meerdere opties: ${suggestions.join(", ")}. Voeg een toevoeging toe.`;
} else {
userErrorMessage = "Huisnummertoevoeging is verplicht.";
}
}
else if (errorCode === "CURL_ERROR" || errorCode === "JSON_ERROR") {
userErrorMessage = "Serverfout. Probeer het later opnieuw.";
}
errorMsg.innerHTML = userErrorMessage;
}
if (data.status === "success") {
// Succes: Modal sluiten en eventueel reload of update
postcodeModal.close();
// Optioneel: location.reload(); of update de pagina content
}
} catch (err) {
console.error("Fetch Failed:", err);
sndBtn.disabled = false;
sndBtnLdr.style.opacity = "0";
sndBtnTxt.style.opacity = "1";
errorMsg.innerHTML = "Er ging iets mis bij het verbinden.";
}
});
</script>
@@ -108,15 +150,14 @@ function render_dialog_html()
<p>Onze prijzen zijn afhankelijk van de regio. Vul daarom de postcode en het huisnummer in om de exacte prijzen te bekijken.</p>
<form id="postcode_modal_form" method="post" action="" novalidate>
<div class="form_fields">
<div>
<!-- Dutch Postcode Field -->
<input
type="text"
name="postcode"
title="Voer een geldige Nederlandse postcode in (bijv. 1234AB of 1234 AB)."
title="Voer een geldige postcode in (bijv. 1234AB of 1234 AB)."
pattern="[1-9][0-9]{3} ?(?!sa|sd|ss)[a-zA-Z]{2}"
placeholder="5010 AA"
size="10"
size="8"
required
autocomplete="off"
/>
@@ -125,16 +166,23 @@ function render_dialog_html()
<input
type="text"
name="huisnummer"
title="Voer een geldig huisnummer in (bijv. 1, 1A, 1-A, 1a)."
pattern="\d+([- ]?[a-zA-Z]+)?"
title="Voer een geldig huisnummer in (bijv. 1, 1A, 1-A)."
pattern="\d+([\- ]?[a-zA-Z]+)?"
placeholder="10"
size="5"
size="3"
required
autocomplete="off"
/>
<button id="postcode_modal_submit" type="submit"><span class="btn_text">OK</span>
<div class="btn_loader">
<svg viewBox="0 0 128 30" xmlns="http://www.w3.org/2000/svg">
<circle style="--i:0" cx="14" cy="15" r="14" />
<circle style="--i:1" cx="64" cy="15" r="14" />
<circle style="--i:2" cx="114" cy="15" r="14" />
</svg>
</div>
</button>
</div>
<button id="postcode_modal_submit" type="submit">OK</button>
</div>
</form>
<div class="error_message_modal_postcode" id="error_message_modal_postcode" aria-live="polite"></div>
</dialog>
@@ -154,56 +202,99 @@ function handle_postcode_modal($data)
$params = $data->get_params();
$nonce = $data->get_header("X-WP-Nonce");
if (wp_verify_nonce($nonce, "wp_rest")) {
if (!verify_postcode($params["postcode"])) {
$resp = [
"status" => "error",
"message" => "postcode",
];
echo json_encode($resp);
exit();
}
if (!verify_huisnummer($params["huisnummer"])) {
$resp = [
"status" => "error",
"message" => "huisnummer",
];
echo json_encode($resp);
exit();
}
$result = getStraatnaam($params["postcode"], $params["huisnummer"]);
if (isset($result["error"])) {
$resp = [
"status" => "error",
"message" => $result["error"],
"apirequest" => "openpostcode.nl",
];
echo json_encode($resp);
exit();
}
$_SESSION["postcode"] = $params["postcode"];
$_SESSION["huisnummer"] = $params["huisnummer"];
$_SESSION["straatnaam"] = $result["straatnaam"];
$_SESSION["woonplaats"] = $result["woonplaats"];
$_SESSION["postcode_is_local"] = postcode_in_range($params["postcode"]);
$resp = [
"status" => "success",
"message" => "all good",
"straatnaam" => $result["straatnaam"],
"lokaal_trarief" => postcode_in_range($params["postcode"]),
];
} else {
$resp = [
"status" => "error",
"message" => "nononce",
];
// 1. Security
if (!wp_verify_nonce($nonce, "wp_rest")) {
echo json_encode(["status" => "error", "message" => "nononce"]);
exit();
}
echo json_encode($resp);
// 2. Basis Validatie
if (!verify_postcode($params["postcode"])) {
echo json_encode(["status" => "error", "message" => "postcode"]);
exit();
}
if (!verify_huisnummer($params["huisnummer"])) {
echo json_encode(["status" => "error", "message" => "huisnummer"]);
exit();
}
// 3. API Call (Ruwe data)
$result = getStraatnaam($params["postcode"], $params["huisnummer"]);
// 4. Validatie & Logic
$hasError = isset($result["error"]);
$errorCode = null;
$errorMessage = null;
$candidates = [];
$straatnaam = null;
$woonplaats = null;
if ($hasError) {
$errorCode = $result["error"]["code"] ?? "UNKNOWN_ERROR";
$errorMessage = $result["error"]["message"] ?? "Onbekende fout";
$candidates = $result["error"]["details"]["candidates"] ?? [];
// SPECIAL CASE: HUISNUMMER_AMBIGUOUS
if ($errorCode === "HUISNUMMER_AMBIGUOUS" && !empty($candidates)) {
$plainMatch = null;
// Zoek naar candidate ZONDER toevoeging
foreach ($candidates as $candidate) {
if (
empty($candidate["huisletter"]) &&
empty($candidate["huisnummertoevoeging"])
) {
$plainMatch = $candidate;
break;
}
}
// Als we een 'platte' match hebben (bijv. 15), dan is het SUCCES
if ($plainMatch) {
$hasError = false; // Negeer de error
$straatnaam = $plainMatch["straat"];
$woonplaats = $plainMatch["woonplaats"];
}
}
} else {
// Normaal succes (geen error in response)
if (!empty($result["results"])) {
$straatnaam = $result["results"][0]["straat"];
$woonplaats = $result["results"][0]["woonplaats"];
} else {
// Edge case: geen error veld, maar ook geen results
$hasError = true;
$errorCode = "NO_RESULTS";
$errorMessage = "Adres niet gevonden";
}
}
// 5. Response sturen
if ($hasError) {
echo json_encode([
"status" => "error",
"code" => $errorCode,
"message" => $errorMessage,
"suggestions" => $result["error"]["details"]["suggestions"] ?? [],
"apirequest" => "openpostcode.nl",
]);
exit();
}
// 6. Succes: Sessie opslaan & response
$_SESSION["postcode"] = $params["postcode"];
$_SESSION["huisnummer"] = $params["huisnummer"];
$_SESSION["straatnaam"] = $straatnaam;
$_SESSION["woonplaats"] = $woonplaats;
$_SESSION["postcode_is_local"] = postcode_in_range($params["postcode"]);
echo json_encode([
"status" => "success",
"message" => "all good",
"straatnaam" => $straatnaam,
"woonplaats" => $woonplaats,
"lokaal_tarief" => postcode_in_range($params["postcode"]),
]);
exit();
}
@@ -246,27 +337,32 @@ function getStraatnaam($postcode, $huisnummer)
curl_setopt($ch, CURLOPT_USERAGENT, "PHP/OpenPostcodeClient");
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if (curl_error($ch)) {
curl_close($ch);
return ["error" => "cURL error: " . curl_error($ch)];
return [
"error" => ["code" => "CURL_ERROR", "message" => curl_error($ch)],
];
}
curl_close($ch);
// Decodeer als associatieve array (true)
$data = json_decode($response, true);
if (isset($data["error"])) {
// Als JSON decode faalt
if (json_last_error() !== JSON_ERROR_NONE) {
return [
"error" => $data["error"]["message"],
"code" => $data["error"]["code"],
"error" => [
"code" => "JSON_ERROR",
"message" => "Invalid JSON response",
],
];
}
return [
"straatnaam" => $data["results"][0]["straat"],
"woonplaats" => $data["results"][0]["woonplaats"],
];
// Geef de RUWE response terug (inclusief meta, error, results, etc.)
return $data;
}
function postcode_in_range($postcode)
@@ -325,7 +421,7 @@ function modify_checkout_with_js()
if (typeof wp !== 'undefined' && wp.data && wp.data.dispatch) {
const store = 'wc/store/cart';
wp.data.dispatch(store).setBillingAddress({
wp.data.dispatch(store).setShippingAddress({
first_name: '',
last_name: '',
address_1: '<?php echo esc_js($address); ?>',
@@ -341,7 +437,7 @@ function modify_checkout_with_js()
//make fields READONLY and ppstcode reset.
setTimeout(() => {
// make prefilled fiields readonly.
$('#billing-postcode, #billing-city, #billing-address_1')
$('#shipping-postcode, #shipping-city, #shipping-address_1')
.prop('readonly', true)
.css('background', '#f9f9f9');
@@ -351,9 +447,8 @@ function modify_checkout_with_js()
div.setAttribute("class", "postcode-reset")
div.innerHTML = `
<a href="#" class="reset-postcode-show-comfirm" >Reset postcode.</a>
<span class="bevestiging"> Weet je het zeker?
<a href="#" class="accept">ja</a>/<a href="#" class="decline">nee</a>
(Deze handeling leegt de winkelwagen.)
<span class="bevestiging"> Deze handeling leegt ook de winkelwagen. Weet je het zeker?
<a href="#" class="accept">&nbsp;&nbsp;JA&nbsp;</a>/<a href="#" class="decline">&nbsp;nee&nbsp;&nbsp;</a>
</span> `;
div.style.width = "100%";
document.querySelector(".wc-block-components-address-form__city").after(div);