ref(env) wip

This commit is contained in:
gcch 2026-04-30 10:56:01 +02:00
commit 730184704e
10 changed files with 58 additions and 36 deletions

View file

@ -17,7 +17,6 @@ use WC_Product;
use function add_action;
use function array_map;
use function assert;
use function base64_encode;
use function is_string;
use function wc_get_products;
use function wp_create_nonce;
@ -33,12 +32,7 @@ $products = array_map(callback: Product::from_wc_product(...), array: $wc_produc
$context['products'] = $products;
// Injecte les états initiaux des données du Produit sous forme de JSON dans le contexte.
$page_states = [
'nonce' => wp_create_nonce('wc_store_api'),
'authString' => base64_encode(
Config::get('WOOCOMMERCE_API_CONSUMER_KEY') . ':' . Config::get('WOOCOMMERCE_API_CONSUMER_SECRET'),
),
]
$page_states = ['authString' => Config::get('WOOCOMMERCE_API_AUTH_STRING'), 'nonce' => wp_create_nonce('wc_store_api')]
|> wp_json_encode(...);
assert(is_string($page_states));
$context['page_states'] = $page_states;

View file

@ -131,7 +131,8 @@ function retire_merdes_wc(): void {
*/
function genere_balises_img_dans_produit_dans_reponse_rest(
WP_REST_Response $response,
mixed $_product,
WC_Data $_product,
WP_REST_Request $_request,
): WP_REST_Response {
// Vérifie que la Réponse a des données
if (empty($response->data)) {
@ -169,37 +170,34 @@ function genere_balises_img_dans_produit_dans_reponse_rest(
return $response;
}
add_filter('woocommerce_rest_prepare_product_object', 'genere_balises_img_dans_produit_dans_reponse_rest', 10, 2);
/**
* TODO.
*/
function genere_prix_maximal_produit_variable_dans_reponse_rest(
WP_REST_Response $reponse,
WC_Data $_produit,
WP_REST_Response $response,
WC_Data $_product,
WP_REST_Request $_request,
): WP_REST_Response {
// Vérifie que la Réponse a des données
if (empty($reponse->data)) {
return $reponse;
if (empty($response->data)) {
return $response;
}
// Si le Produit n'est pas Variable, assigner le prix du Produit comme prix maximal
if ('variable' !== $reponse->data['type']) {
$reponse->data['prix_maximal'] = $reponse->data['regular_price'];
if ('variable' !== $response->data['type']) {
$response->data['prix_maximal'] = $response->data['regular_price'];
return $reponse;
return $response;
}
// Assigne le prix de la Variation la plus chère dans la Réponse
$reponse->data['prix_maximal'] = collect($reponse->data['variations'])
$response->data['prix_maximal'] = collect($response->data['variations'])
->map(wc_get_product(...))
->map(static fn($p) => $p->get_price())->max();
return $reponse;
return $response;
}
add_filter('woocommerce_rest_prepare_product_object', 'genere_prix_maximal_produit_variable_dans_reponse_rest', 10, 2);
/**
* Retire la propagande commerciale de WooCommerce du menu.
*/
@ -216,3 +214,8 @@ add_action('init', 'retire_script_galerie');
add_action('template_redirect', 'retire_merdes_wc');
add_action('wp_enqueue_scripts', 'dequeue_woocommerce_styles_scripts');
add_filter('woocommerce_enqueue_styles', '__return_empty_array');
add_filter('woocommerce_rest_prepare_product_object', 'genere_balises_img_dans_produit_dans_reponse_rest', 10, 3);
add_filter('woocommerce_rest_prepare_product_object', 'genere_prix_maximal_produit_variable_dans_reponse_rest', 10, 3);
// DEBUG
// add_filter('woocommerce_store_api_disable_nonce_check', '__return_true');

View file

@ -1,4 +1,4 @@
import { Console, Context, Effect, Layer, Match, pipe, References, Schedule, Schema, SchemaIssue } from "effect";
import { Console, Context, Effect, Layer, Match, pipe, Schedule, Schema, SchemaIssue } from "effect";
import type { SchemaError } from "effect/Schema";
import type {
HttpClientError,
@ -13,6 +13,7 @@ import { HttpClientErrorSchema } from "effect/unstable/http/HttpClientError";
import type { CartProduct, GetProducts } from "../schemas/api.ts";
import { Product } from "../schemas/api.ts";
import { WooCommerceCart } from "../schemas/cart.ts";
import { AppConfig, Provider } from "./config.ts";
/** Le nombre maximal d'essais pour une Requête. */
const MAX_RETRIES = 3;
@ -81,6 +82,8 @@ class APIClient extends Context.Service<APIClient>()("haikuatelier.fr/APIClient"
}),
);
const config = yield* AppConfig.parse(Provider);
const matchAPIError = (error: HttpClientError.HttpClientError | SchemaError): APIError => {
if (error._tag === "SchemaError") {
return new APIRequestError({
@ -126,9 +129,6 @@ class APIClient extends Context.Service<APIClient>()("haikuatelier.fr/APIClient"
Effect.flatMap(HttpClientResponse.schemaBodyJson(WooCommerceCart)),
Effect.mapError(error => matchAPIError(error)),
Effect.tapError(error => printErrorAsSuccinctMessage(error)),
// Effect.catchTag("APIResponseError", error => {
// if (error.cause.)
// }),
);
return response;
@ -142,8 +142,8 @@ class APIClient extends Context.Service<APIClient>()("haikuatelier.fr/APIClient"
HttpClientRequest.setHeader("Nonce", nonce),
// TODO: Utiliser l'environnement
HttpClientRequest.basicAuth(
"ck_ed966a2265099a6dfe9915db692cbd2450cceed6",
"cs_a046c91647af95188a3e39a736ebe02f2024e430",
config.WOOCOMMERCE_API_CONSUMER_KEY,
config.WOOCOMMERCE_API_CONSUMER_SECRET,
),
// Le corps de la Requête a été validée en amont, on peut utiliser Unsafe.
HttpClientRequest.setUrlParams(queryParams),

View file

@ -0,0 +1,17 @@
/**
* `Config<A>` décrit la Configuration nécessaire. `ConfigProvider` est le _backend_ qui la charge. Par défault, la Configuration est lue depuis les variables d'environnement, mais d'autres sources peuvent être utilisées.
*/
import { Config, ConfigProvider } from "effect";
const AppConfig = Config.all({
WOOCOMMERCE_API_CONSUMER_KEY: Config.redacted("WOOCOMMERCE_API_CONSUMER_KEY"),
WOOCOMMERCE_API_CONSUMER_SECRET: Config.redacted("WOOCOMMERCE_API_CONSUMER_SECRET"),
});
const Provider = ConfigProvider.fromUnknown({
WOOCOMMERCE_API_CONSUMER_KEY: "ck_329c944b248aa7cc837c7662d9c6e09d638802df",
WOOCOMMERCE_API_CONSUMER_SECRET: "cs_5687d0c694bd519b231145afa7177c0c987f7155",
});
export { AppConfig, Provider };

View file

@ -11,7 +11,7 @@ const ShopPageRuntime = ManagedRuntime.make(
Layer.provideMerge(ShopPageMessages.Live),
Layer.provideMerge(ShopPageElements.Live),
Layer.provide(APIClient.Live),
Layer.tapError(error => Console.error("ProductPageRuntime", "Impossible de créer le Layer :", error)),
Layer.tapError(error => Console.error("ProductPageRuntime", "Impossible de créer le Layer :", error.message)),
),
);
export default ShopPageRuntime;

View file

@ -1,6 +1,5 @@
import {
Array as FxArray,
Console,
Context,
Effect,
Layer,
@ -118,8 +117,6 @@ class ShopPageDOM extends Context.Service<ShopPageDOM>()("haikuatelier.fr/Shop/S
};
const onMoreProductsWantedHandler = Effect.fn("onMoreProductsWantedHandler")(function*() {
yield* Console.debug("onMoreProductsWantedHandler");
/** Le numéro de page souhaitée. */
const newPageNumber = yield* Ref.updateAndGet(PageNumber, pageNumber => pageNumber + 1);
/** L'ID de la Catégorie de Produits affichée dans la page si elle existe. */
@ -137,7 +134,6 @@ class ShopPageDOM extends Context.Service<ShopPageDOM>()("haikuatelier.fr/Shop/S
yield* SubscriptionRef.set(ShowMoreButtonText, "Getting Products...");
const newProducts = yield* API.GetProducts(nonce, requestBody);
yield* Console.debug("onMoreProductsWantedHandler", newProducts);
// Rétablis le texte du Bouton et réactive les interactions.
yield* SubscriptionRef.set(ShowMoreButtonText, "Show more");

View file

@ -9,17 +9,14 @@ import ShopPageElements from "./page-boutique/service-elements.ts";
import ShopPageMessages from "./page-boutique/service-messages.ts";
document.addEventListener("DOMContentLoaded", (): void => {
console.debug("scripts-page-boutique");
// initialisePageBoutique();
ShopPageRuntime.runFork(Effect.gen(function*() {
const Elements = yield* ShopPageElements;
const DOM = yield* ShopPageDOM;
const Messages = yield* ShopPageMessages;
yield* Effect.all([DOM.initLoadMoreProductsOnButtonClick(), Messages.initShowMoreButtonUpdates()], {
yield* Effect.all([DOM.initMoreProductsOnButtonClick(), Messages.initShowMoreButtonUpdates()], {
concurrency: "unbounded",
}).pipe(Effect.tapCause(Console.error));
console.debug(Elements.ProductsGrid);
}));
});