diff --git a/frontend-admin/app.json b/frontend-admin/app.json index 969ca7d5..48252ce8 100644 --- a/frontend-admin/app.json +++ b/frontend-admin/app.json @@ -2,7 +2,7 @@ "expo": { "name": "Admin Panel", "slug": "frontend-admin", - "version": "1.0.0", + "version": "1.0.1", "orientation": "portrait", "icon": "./assets/icon.png", "userInterfaceStyle": "dark", @@ -53,7 +53,7 @@ } }, "owner": "xor290", - "runtimeVersion": "admin-1.0.0", + "runtimeVersion": "admin-1.0.1", "updates": { "url": "https://u.expo.dev/fcb7a0bc-5b2f-453d-ba16-9f97d9f0e440", "codeSigningCertificate": "./certs/certificate.pem", diff --git a/frontend-admin/certs/certificate.pem b/frontend-admin/certs/certificate.pem index 3eaf59ca..01597965 100644 --- a/frontend-admin/certs/certificate.pem +++ b/frontend-admin/certs/certificate.pem @@ -1,19 +1,19 @@ ------BEGIN CERTIFICATE----- -MIIDAzCCAeugAwIBAgIJR6I83gTOhZ0JMA0GCSqGSIb3DQEBCwUAMCsxKTAnBgNV -BAMTIE1pbGlldSBOYW50YWlzIC8gVWJlciBTdHVwIEFkbWluMB4XDTI2MDgxNjEy -MTcwNFoXDTM2MDgxNjEyMTcwNFowKzEpMCcGA1UEAxMgTWlsaWV1IE5hbnRhaXMg -LyBVYmVyIFN0dXAgQWRtaW4wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB -AQDLGiJBJLWXr/qbjTpsLGK1++qjebpviWUAUp5f2nBEXBFohtrGsm7CE9bpfhWt -2pSTLw6faASUSafAwT0JUaPTFb7kvwmhl6nteWs+L5doTSzuyVwNu4l/29JoAUOZ -gDgxJJNBovR5X5raBG7Q18E7/KrSskw7iMuYCa50rlKD5BLko4IoWBVV4ik6IQN1 -LdlIIZNoMYc910rKq5NEGMs0m2Q3e4LUiP1sBXyZFAcRWaGY/cZ9yvN0OKk/q/bb -fyD2EZ+/lcX+PqBmSKY0/D+k9YGXW8NRces/1LbVBs8daPGoc6tsCFaaJbw76aWT -mCx5lFDiSwrkF9PSKCcnVFWLAgMBAAGjKjAoMA4GA1UdDwEB/wQEAwIHgDAWBgNV -HSUBAf8EDDAKBggrBgEFBQcDAzANBgkqhkiG9w0BAQsFAAOCAQEAb4koejHwa86+ -rFaL+jsiAifV8KkgUde9QiK2/EVM5aUcyk+CAeAsUopftnOQpo1JlNCQ1nxMY183 -s1K4DMQxs5xeYl/JD71o+qYCB0mYcqubYLmsc9Vellrzrz5eh83PUijgQU30htdm -w4qeUY5CP5ZlrlIlMa8P0Fab7327r2LEbUnrMlo3pHcSayLkqtKeQ//vk8M0TFIK -Fh1hnM3WzvdrLUyqsKOKjGve3QYfSz4OdUiSx2fedcx8xJeMTebaxj9VD5Uc2r1U -RFy4V6czONoAISFY5+AoqsNewtYoQ4UYuQlBFxNaNC+OIsENdhhFFrtWV9XylfDG -QcU3SMKMJg== ------END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDAzCCAeugAwIBAgIJR6I83gTOhZ0JMA0GCSqGSIb3DQEBCwUAMCsxKTAnBgNV +BAMTIE1pbGlldSBOYW50YWlzIC8gVWJlciBTdHVwIEFkbWluMB4XDTI2MDgxNjEy +MTcwNFoXDTM2MDgxNjEyMTcwNFowKzEpMCcGA1UEAxMgTWlsaWV1IE5hbnRhaXMg +LyBVYmVyIFN0dXAgQWRtaW4wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB +AQDLGiJBJLWXr/qbjTpsLGK1++qjebpviWUAUp5f2nBEXBFohtrGsm7CE9bpfhWt +2pSTLw6faASUSafAwT0JUaPTFb7kvwmhl6nteWs+L5doTSzuyVwNu4l/29JoAUOZ +gDgxJJNBovR5X5raBG7Q18E7/KrSskw7iMuYCa50rlKD5BLko4IoWBVV4ik6IQN1 +LdlIIZNoMYc910rKq5NEGMs0m2Q3e4LUiP1sBXyZFAcRWaGY/cZ9yvN0OKk/q/bb +fyD2EZ+/lcX+PqBmSKY0/D+k9YGXW8NRces/1LbVBs8daPGoc6tsCFaaJbw76aWT +mCx5lFDiSwrkF9PSKCcnVFWLAgMBAAGjKjAoMA4GA1UdDwEB/wQEAwIHgDAWBgNV +HSUBAf8EDDAKBggrBgEFBQcDAzANBgkqhkiG9w0BAQsFAAOCAQEAb4koejHwa86+ +rFaL+jsiAifV8KkgUde9QiK2/EVM5aUcyk+CAeAsUopftnOQpo1JlNCQ1nxMY183 +s1K4DMQxs5xeYl/JD71o+qYCB0mYcqubYLmsc9Vellrzrz5eh83PUijgQU30htdm +w4qeUY5CP5ZlrlIlMa8P0Fab7327r2LEbUnrMlo3pHcSayLkqtKeQ//vk8M0TFIK +Fh1hnM3WzvdrLUyqsKOKjGve3QYfSz4OdUiSx2fedcx8xJeMTebaxj9VD5Uc2r1U +RFy4V6czONoAISFY5+AoqsNewtYoQ4UYuQlBFxNaNC+OIsENdhhFFrtWV9XylfDG +QcU3SMKMJg== +-----END CERTIFICATE----- diff --git a/frontend-admin/eas.json b/frontend-admin/eas.json index 815870c6..aab6d181 100644 --- a/frontend-admin/eas.json +++ b/frontend-admin/eas.json @@ -4,6 +4,18 @@ "appVersionSource": "local" }, "build": { + "development": { + "developmentClient": true, + "distribution": "internal", + "android": { + "buildType": "apk", + "gradleCommand": ":app:assembleDebug" + }, + "env": { + "EXPO_PUBLIC_API_URL": "http://localhost:8080" + }, + "channel": "development" + }, "pre-prod": { "distribution": "internal", "android": { @@ -11,7 +23,7 @@ }, "env": { "EXPO_PUBLIC_API_URL": "https://uber-demo.club", - "EXPO_PUBLIC_UPDATE_URL": "https://ota-preprod.uber-stup.club/api/manifest" + "EXPO_PUBLIC_UPDATE_URL": "https://ota.uber-stup.club/api/manifest" }, "channel": "pre-prod-admin" }, @@ -23,7 +35,7 @@ }, "env": { "EXPO_PUBLIC_API_URL": "https://mln-uber.club", - "EXPO_PUBLIC_UPDATE_URL": "https://ota-prod.uber-stup.club/api/manifest" + "EXPO_PUBLIC_UPDATE_URL": "https://ota.uber-stup.club/api/manifest" }, "channel": "production-admin" } diff --git a/frontend-admin/src/api/api_admin.ts b/frontend-admin/src/api/api_admin.ts index 3584518c..c442501c 100644 --- a/frontend-admin/src/api/api_admin.ts +++ b/frontend-admin/src/api/api_admin.ts @@ -1109,6 +1109,7 @@ export interface PointsTier { export interface RewardCategoryConfig { category: string; + type: "free_product" | "half_price_product"; all_products: boolean; product_ids: number[]; } @@ -1121,7 +1122,6 @@ export interface RewardItem { export interface PointsReward { threshold: number; - type: "free_product" | "half_price_product" | "custom"; description: string; category_configs: RewardCategoryConfig[]; reward_items: RewardItem[]; diff --git a/frontend-admin/src/screens/admin/SettingsScreen.tsx b/frontend-admin/src/screens/admin/SettingsScreen.tsx index bca1526a..e58d03a3 100644 --- a/frontend-admin/src/screens/admin/SettingsScreen.tsx +++ b/frontend-admin/src/screens/admin/SettingsScreen.tsx @@ -686,14 +686,13 @@ function TiersSection({ const REWARD_ACCENT = "#f59e0b"; -const REWARD_TYPES: { value: PointsReward["type"]; label: string; icon: string }[] = [ +const REWARD_TYPES: { value: RewardCategoryConfig["type"]; label: string; icon: string }[] = [ { value: "free_product", label: "Produit offert", icon: "gift-outline" }, { value: "half_price_product", label: "Produit à -50%", icon: "pricetag-outline" }, ]; const EMPTY_REWARD: PointsReward = { threshold: 20, - type: "free_product", description: "", category_configs: [], reward_items: [], @@ -822,7 +821,7 @@ function CentralRewardSection({ const getCatConfig = (catName: string): RewardCategoryConfig => r.category_configs.find((c) => c.category === catName) ?? - { category: catName, all_products: true, product_ids: [] }; + { category: catName, type: "free_product", all_products: true, product_ids: [] }; const isCatSelected = (catName: string) => r.category_configs.some((c) => c.category === catName); @@ -831,7 +830,7 @@ function CentralRewardSection({ if (isCatSelected(catName)) { update({ category_configs: r.category_configs.filter((c) => c.category !== catName) }); } else { - update({ category_configs: [...r.category_configs, { category: catName, all_products: true, product_ids: [] }] }); + update({ category_configs: [...r.category_configs, { category: catName, type: "free_product", all_products: true, product_ids: [] }] }); } }; @@ -894,34 +893,6 @@ function CentralRewardSection({ - {/* Type */} - - Type de récompense - - {REWARD_TYPES.map((rt) => { - const sel = r.type === rt.value; - return ( - update({ type: rt.value })} - style={{ - flexDirection: "row", alignItems: "center", gap: spacing.xs, - paddingHorizontal: spacing.m, paddingVertical: spacing.s, - borderRadius: borderRadius.sm, borderWidth: 1.5, - borderColor: sel ? REWARD_ACCENT : colors.border, - backgroundColor: sel ? REWARD_ACCENT + "20" : "transparent", - }} - > - - - {rt.label} - - - ); - })} - - - {/* Description */} Description affichée au client @@ -935,12 +906,12 @@ function CentralRewardSection({ /> - {/* Produits récompense — uniquement pour le type "Produit offert" */} - {r.type === "free_product" && ( + {/* Produits récompense — proposés au client selon le type choisi + pour la catégorie de chaque produit (voir "Catégories éligibles" ci-dessous) */} Produits ajoutés au panier - Quand le client réclame sa récompense, ces produits sont automatiquement ajoutés à son panier (gratuits). Il doit commander au moins un produit normal. + Quand le client réclame sa récompense, ces produits sont automatiquement ajoutés à son panier — gratuits ou à -50% selon le type configuré pour la catégorie du produit. Il doit commander au moins un produit normal. {r.reward_items.map((item, idx) => { @@ -1062,14 +1033,14 @@ function CentralRewardSection({ )} - )} - {/* Catégories éligibles — uniquement pour le type "Produit à -50%" */} - {r.type === "half_price_product" && ( + {/* Catégories éligibles — chaque catégorie choisit son propre type + (produit offert ou -50%), qui s'applique aux produits récompense + de cette catégorie configurés ci-dessus */} - Catégories éligibles à -50% + Catégories éligibles - Sélectionnez les catégories, puis pour chacune choisissez tous les produits ou une sélection. + Sélectionnez les catégories, choisissez le type de récompense pour chacune, puis tous les produits ou une sélection. {allCategories.length === 0 ? ( Aucune catégorie disponible @@ -1103,15 +1074,41 @@ function CentralRewardSection({ /> - {/* Sélecteur produits (visible si catégorie sélectionnée) */} + {/* Type + sélecteur produits (visible si catégorie sélectionnée) */} {selected && ( - + + + {REWARD_TYPES.map((rt) => { + const cfg = getCatConfig(cat.name); + const sel = (cfg.type || "free_product") === rt.value; + return ( + updateCatConfig({ ...cfg, type: rt.value })} + style={{ + flexDirection: "row", alignItems: "center", gap: 4, + paddingHorizontal: spacing.s, paddingVertical: 4, + borderRadius: borderRadius.sm, borderWidth: 1.5, + borderColor: sel ? REWARD_ACCENT : colors.border, + backgroundColor: sel ? REWARD_ACCENT + "22" : "transparent", + }} + > + + + {rt.label} + + + ); + })} + + + )} ); @@ -1119,25 +1116,23 @@ function CentralRewardSection({ )} - )} {/* Récapitulatif */} {(r.category_configs.length > 0 || r.reward_items.filter((it) => it.product_id > 0).length > 0) && ( Récapitulatif - Dès {r.threshold} pts par type →{" "} - {REWARD_TYPES.find((x) => x.value === r.type)?.label} + Dès {r.threshold} pts par type de points → récompense débloquée {r.description !== "" && ( "{r.description}" )} - {r.type === "half_price_product" && r.category_configs.map((cfg) => ( + {r.category_configs.map((cfg) => ( - • Éligible à -50% : {cfg.category} — {cfg.all_products ? "tous les produits" : `${cfg.product_ids.length} produit(s)`} + • {REWARD_TYPES.find((x) => x.value === (cfg.type || "free_product"))?.label} : {cfg.category} — {cfg.all_products ? "tous les produits" : `${cfg.product_ids.length} produit(s)`} ))} - {r.type === "free_product" && r.reward_items.filter((it) => it.product_id > 0).map((it, idx) => { + {r.reward_items.filter((it) => it.product_id > 0).map((it, idx) => { const prod = Object.values(productsByCategory).flat().find((p) => p.id === it.product_id); return ( diff --git a/frontend-admin/src/screens/delivery/DashboardScreen.tsx b/frontend-admin/src/screens/delivery/DashboardScreen.tsx index 734ad1a5..626da74d 100644 --- a/frontend-admin/src/screens/delivery/DashboardScreen.tsx +++ b/frontend-admin/src/screens/delivery/DashboardScreen.tsx @@ -715,7 +715,7 @@ export default function DashboardScreen() { {prod.is_reward && ( - Offert + Récompense )} @@ -724,7 +724,7 @@ export default function DashboardScreen() { - {prod.is_reward ? "Offert" : `${(prod.prix ?? 0).toFixed(2)}€`} + {prod.is_reward && (prod.prix ?? 0) === 0 ? "Offert" : `${(prod.prix ?? 0).toFixed(2)}€`} ))} @@ -2018,7 +2018,7 @@ export default function DashboardScreen() { {prod.is_reward && ( - Offert + Récompense )} @@ -2027,7 +2027,7 @@ export default function DashboardScreen() { - {prod.is_reward ? "Offert" : `${(prod.prix ?? 0).toFixed(2)}€`} + {prod.is_reward && (prod.prix ?? 0) === 0 ? "Offert" : `${(prod.prix ?? 0).toFixed(2)}€`} ))} diff --git a/mobile/certs/certificate.pem b/mobile/certs/certificate.pem index a53e9f6b..59a09761 100644 --- a/mobile/certs/certificate.pem +++ b/mobile/certs/certificate.pem @@ -1,18 +1,18 @@ -----BEGIN CERTIFICATE----- -MIIC9zCCAd+gAwIBAgIJOs/S1ceI7Zq6MA0GCSqGSIb3DQEBCwUAMCUxIzAhBgNV -BAMTGk1pbGlldSBOYW50YWlzIC8gVWJlciBTdHVwMB4XDTI2MDcxMTIwNDI1MloX -DTM2MDcxMTIwNDI1MlowJTEjMCEGA1UEAxMaTWlsaWV1IE5hbnRhaXMgLyBVYmVy -IFN0dXAwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQClkcj0h9gBuq9z -FVT1UwhBto2sTZglO3iwsgyPWx7I7twyY+kUU57rgHzI7YGieX599xM4oGjau8r7 -PjPK1160djTuRa9bWXnYBnotaU0Hp3rOicxbMygCGQoZtDqxRUMo4HxrBSYnZaVo -VYPqs/utSTA43El7SrzJddxBZK4WbJbfgdXDYrdeLz4Syrdx8DXBnCYmmmHhpQsE -orykYCUi7qd0CJi6kZGVOgR+Hq0B581DqnUA2H3iQWdk/0EZf6PN/gR0f9YlH3oN -N8tYLo7TOScSmUNJ5T2hFEuWuS/O6JKUI6a7MpIOv7XYxNDYWT/Ae9QNT8GcqVcW -39j2xSvvAgMBAAGjKjAoMA4GA1UdDwEB/wQEAwIHgDAWBgNVHSUBAf8EDDAKBggr -BgEFBQcDAzANBgkqhkiG9w0BAQsFAAOCAQEAj+SMnO7/IFajlg6Uo6aJotM8vdPp -4Dgi18DURZ0evUsxm4lLHWQ//6zF8jVaqPwsKA9IuPW+8O8gC8iwCY3YfjbaC5Ad -vPlvpzU6bvaA//utLVVUlfkk87vs5QotJkshoImJJoDPfO/Q1yv1qrMHXPnGyyzV -K0K3rYeXVYMDeJ9y2742D+MEg0Zse7xmNcde2z5aUuFlK7ORBs03FohD2U5zUqUg -jaia/wN4lIMCdJJmoPRUydbLJ8yVns9whFxXU1eGqaFf27jBdI/nPMVmO1YPsxnk -VvOiP6n1T+aZ7qaeOY9hsSmJ9FBeh3pOtRrdxmA8wBxD3zAORfLl89mjsw== +MIIC9zCCAd+gAwIBAgIJdh2LLVj8WJMXMA0GCSqGSIb3DQEBCwUAMCUxIzAhBgNV +BAMTGk1pbGlldSBOYW50YWlzIC8gVWJlciBTdHVwMB4XDTI2MDgxNjEyMTA0OVoX +DTM2MDgxNjEyMTA0OVowJTEjMCEGA1UEAxMaTWlsaWV1IE5hbnRhaXMgLyBVYmVy +IFN0dXAwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCsUjXhTk71qOWK +XB+9jJ9FiP6+FXrPM9fj9a/75XjTNtIfS55m35rlkz4cfZvtjW1XziyMe+86/wGM +fg3iG8Zvu4DOS4aONFBz8kWosi6p2AAIPUQVIEdCEjL0V5r0MYfHvFu+GCLVbPeQ +hrd11Y6HwWEhCsKsK3OQXlN8PK9cpzeSeoCNdzA1iseFuAXoPu9Gm88lLThJgEvx +z95/piTa3OavRaYraP+ytd3/f87XbVrbtRGJq6sPIV9FHBt4z4AXcGwUIqTQ1Gwi +kMOaROMlQpzYlKS+yoZ72p6xs9dF0kBKsitlbavJ2E7ZrVoQh0Ufr4/6X26kKGwH +BvaH3E9vAgMBAAGjKjAoMA4GA1UdDwEB/wQEAwIHgDAWBgNVHSUBAf8EDDAKBggr +BgEFBQcDAzANBgkqhkiG9w0BAQsFAAOCAQEAE5sO2Nvgum66dib70UsbxTO7/8Os +yHy/RT7zUsgeVVGK18nIbC/SiZVQ9FNLw3ps0bIrXX968Ym4KJ1di5gcw5nuiVpf +1QNiN0t7vS6Gxaj2l9/TwoL409ud1kMZ2lXKyHnwVaNontNt7Y+e6HnOLeYNEepn +5v8qfwOa0H3e6o2T71uCepvVLCbmIngnpF5APkDNUbGnJFpoyxrrleNboQ8CZYY4 +uEsrPBZSRlhWLMLsJQOgLiRlnAYgqxS9C3DlxpZbsvA9eIDJuPquzUOuNX1KTMQl +My9rR/pXYvwd7xuOD+crkGRL6bLW9HRq9FWKT6JWEkNSagmyGxcPv/ilgQ== -----END CERTIFICATE-----