chore: build
This commit is contained in:
@@ -30,6 +30,7 @@ yarn-error.*
|
||||
.DS_Store
|
||||
*.pem
|
||||
!certs/certificate.pem
|
||||
!certs/certificate-preprod.pem
|
||||
|
||||
# local env files
|
||||
.env*.local
|
||||
|
||||
+2
-2
@@ -2,7 +2,7 @@
|
||||
"expo": {
|
||||
"name": "Milieu Nantais",
|
||||
"slug": "frontend-client",
|
||||
"version": "1.0.2",
|
||||
"version": "1.0.0",
|
||||
"orientation": "portrait",
|
||||
"icon": "./assets/icon.png",
|
||||
"userInterfaceStyle": "dark",
|
||||
@@ -52,7 +52,7 @@
|
||||
"router": {}
|
||||
},
|
||||
"owner": "xor290",
|
||||
"runtimeVersion": "client-1.0.2",
|
||||
"runtimeVersion": "client-1.0.0",
|
||||
"updates": {
|
||||
"url": "https://u.expo.dev/110d06c8-a8d5-4b3c-b262-0d4d7509ae9d",
|
||||
"codeSigningCertificate": "./certs/certificate.pem",
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDHTCCAgWgAwIBAgIUfteDb4QnVA4dHw75YlHAKlxv7e0wDQYJKoZIhvcNAQEL
|
||||
BQAwIzEhMB8GA1UEAwwYVWJlciBTdHVwIENsaWVudCBQcmVwcm9kMB4XDTI2MDgy
|
||||
NjEwMjI0NVoXDTM2MDgyMzEwMjI0NVowIzEhMB8GA1UEAwwYVWJlciBTdHVwIENs
|
||||
aWVudCBQcmVwcm9kMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvc/P
|
||||
p8uCt0SHb+tCaM/Pi7wm8QBKf6qnSnFr+peHmM3xWZkSEk7v4NelTlwcJ/A+Azfv
|
||||
0Py7euIGdOU13bXZRSDP5wbXVOJJt1eftJsiWlOT6ehGrnZOHd+telnTnl/fWbjJ
|
||||
qtDphpt3bm0DfxUypatG/NAnQ1SEiLMyUwiBTrIWoLFQ+XbC6ULnoKfhROqXj1h7
|
||||
eR+xCJ28R+LuB+kJk8EhD8L4CZqlO/xVk93eN3oJuTHJYT7jWff2uT+1SczRVvv4
|
||||
ZmnznU/gUPXJcQlISnmvaG/+8Ng8Z9ThDKDnnneJpFXAhFqU62Wjb/Y9rTb9FWZn
|
||||
saBQuDZGp+nBVHcT5QIDAQABo0kwRzAOBgNVHQ8BAf8EBAMCB4AwFgYDVR0lAQH/
|
||||
BAwwCgYIKwYBBQUHAwMwHQYDVR0OBBYEFE54b6JtwnO9lw5asoSxEQ77B3FHMA0G
|
||||
CSqGSIb3DQEBCwUAA4IBAQA/UDUlMYaYaArtYl/BEKSj7jZTC3gFRA8393XLFdUi
|
||||
/roiIdd6suX+T957wgXRSTpGPfFVO+azJChosEKMRI477r0vWRX4J8B0GXNo+jcr
|
||||
okMjt5cY6G1egTvl+slJANoevJAClgOVOZ/+HShB0k9i9sIJf/rViKj7OV19UEur
|
||||
0m2gK/qdvxbeFuw2RUq5tFRgUZzL8TyZmbJVKu0iRX4wB1MuUezDlr5a/k1qd27V
|
||||
24U0+IiAQTjTVZj1ab8k6oP6376p6ydKoL2JLR7A/f/B1y/TojJbDNsU5EMCMt64
|
||||
TRZ0aeslLtcnynqlpzr3JNXugtRPaz2WxT8NmB8H6fQR
|
||||
-----END CERTIFICATE-----
|
||||
@@ -1,18 +1,19 @@
|
||||
-----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==
|
||||
-----END CERTIFICATE-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDDTCCAfWgAwIBAgIUJrRf0VNrabHd9GaoW3iBxw8RzygwDQYJKoZIhvcNAQEL
|
||||
BQAwGzEZMBcGA1UEAwwQVWJlciBTdHVwIENsaWVudDAeFw0yNjA4MjYxMDIyNDVa
|
||||
Fw0zNjA4MjMxMDIyNDVaMBsxGTAXBgNVBAMMEFViZXIgU3R1cCBDbGllbnQwggEi
|
||||
MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC2foB+W04MkXIyANrUeffDy1Vo
|
||||
soZ6UgD/FWvvzYY0Pf29kWsatBTfuwfMQPg4VVl2OXci7oL01Steml2ZOMb+kF0f
|
||||
n3da5dRFFPVEp7KI+y1bRSBcy3M79Y8oon2Q1TcooCeaKVXVx7Ykg6/GPDLL/+tI
|
||||
0HVXdtQxMDr1EcCZuTo2g+91o84MLXJXipHkuS64UYAjlPrJFcq9jR4TJ7zYsL01
|
||||
P9fPmokAm2Vc2B9dG+BjlSBXp7oyLIOtMwC1zACkWiU+81d59NMZAv04FJENB/P6
|
||||
xeG3WqM/n/9INGy2Xvd9wkUuZEObZJiNh4CL6ZWzhJPCPq2cHcqrcHxXeRtxAgMB
|
||||
AAGjSTBHMA4GA1UdDwEB/wQEAwIHgDAWBgNVHSUBAf8EDDAKBggrBgEFBQcDAzAd
|
||||
BgNVHQ4EFgQUcyo31IaveB8XCpMt/m0rLvC/kk4wDQYJKoZIhvcNAQELBQADggEB
|
||||
ACVgBn04MtRN/VysKaus837+x5XtiXm+V6Bi57+JkqORKEgzV2PdmFCtpcG6ePef
|
||||
0uUVkK7IF2tGm1AfNUkwvw/CoKNaFe9rtcNMLVYZSDbn6KOAyBSAxb2yQewJaSLN
|
||||
/qpjkg45Jrcwyl0cQ6tQfQgWmliXE1AbgAN5j0foKA0b4ioLsI0dPFncYo5hzmOb
|
||||
9FQ7QGbHwiAMmnQ3PHbpoby6DVpmEeuIj22FgAxt9TI7bYon/OHVO894jN0CCEOJ
|
||||
8wrAUxgIGJKLFYSQdodaQ4WESyYnAcGTneqypC+l9yJQBWNUT+cJeNaQmu47Zbra
|
||||
eLmJVdPXnnRe2UT7wSa02xg=
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
+2
-2
@@ -23,7 +23,7 @@
|
||||
},
|
||||
"env": {
|
||||
"EXPO_PUBLIC_API_URL": "https://uber-demo.club",
|
||||
"EXPO_PUBLIC_UPDATE_URL": "https://ota.uber-stup.club/api/manifest"
|
||||
"EXPO_PUBLIC_UPDATE_URL": "https://ota-mobile-preprod.uber-stup.club/api/manifest"
|
||||
},
|
||||
"channel": "pre-prod-client"
|
||||
},
|
||||
@@ -35,7 +35,7 @@
|
||||
},
|
||||
"env": {
|
||||
"EXPO_PUBLIC_API_URL": "https://mln-uber.club",
|
||||
"EXPO_PUBLIC_UPDATE_URL": "https://ota.uber-stup.club/api/manifest"
|
||||
"EXPO_PUBLIC_UPDATE_URL": "https://ota-mobile-prod.uber-stup.club/api/manifest"
|
||||
},
|
||||
"channel": "production-client"
|
||||
}
|
||||
|
||||
@@ -529,7 +529,6 @@ export const getOrdersWithTracking = async () => {
|
||||
|
||||
// ============================================
|
||||
// HISTORY
|
||||
// ============================================
|
||||
|
||||
export const getMyCompletedOrders = async (): Promise<HistoryResponse> => {
|
||||
try {
|
||||
@@ -962,13 +961,18 @@ export const toggle2FA = async (
|
||||
// 🏆 POINTS — RÉCOMPENSES
|
||||
// ============================================
|
||||
|
||||
export type RewardConfigProduct = {
|
||||
product_id: number;
|
||||
product_name: string;
|
||||
quantity: number;
|
||||
};
|
||||
|
||||
export type RewardCategoryConfig = {
|
||||
category: string;
|
||||
type: "free_product" | "half_price_product";
|
||||
all_products: boolean;
|
||||
product_ids: number[];
|
||||
product_names: string[];
|
||||
amount: number;
|
||||
products: RewardConfigProduct[];
|
||||
quantity: number;
|
||||
};
|
||||
|
||||
export type RewardItemConfig = {
|
||||
|
||||
@@ -355,6 +355,8 @@ export interface ProductPrice {
|
||||
quantity: number;
|
||||
price: number;
|
||||
active_price?: boolean;
|
||||
promo_price?: number; // prix réduit si une promotion couvre ce palier
|
||||
promo_percent?: number; // pourcentage de réduction appliqué
|
||||
}
|
||||
export interface Product {
|
||||
id: number;
|
||||
|
||||
@@ -32,7 +32,6 @@ export const getRole = () => AsyncStorage.getItem(ROLE_KEY);
|
||||
export const setRole = (role: string) => AsyncStorage.setItem(ROLE_KEY, role);
|
||||
export const removeRole = () => AsyncStorage.removeItem(ROLE_KEY);
|
||||
|
||||
// Clear all auth data
|
||||
export const clearAllAuth = async () => {
|
||||
await AsyncStorage.multiRemove([
|
||||
TOKEN_KEY,
|
||||
|
||||
@@ -830,14 +830,14 @@ export default function OrderHistoryScreen() {
|
||||
</View>,
|
||||
]
|
||||
: (
|
||||
cfg.product_names ??
|
||||
cfg.products ??
|
||||
[]
|
||||
).map(
|
||||
(
|
||||
name,
|
||||
p,
|
||||
) => (
|
||||
<View
|
||||
key={`${cfg.category}-${name}`}
|
||||
key={`${cfg.category}-${p.product_id}`}
|
||||
style={
|
||||
styles.rewardAmountBadge
|
||||
}
|
||||
@@ -848,7 +848,7 @@ export default function OrderHistoryScreen() {
|
||||
}
|
||||
>
|
||||
{
|
||||
name
|
||||
p.product_name
|
||||
}
|
||||
</Text>
|
||||
</View>
|
||||
|
||||
@@ -63,12 +63,18 @@ export default function ProductDetailScreen() {
|
||||
quantity: parseFloat(String(pr.quantity)),
|
||||
price: parseFloat(String(pr.price)),
|
||||
active_price: pr.active_price,
|
||||
promo_price:
|
||||
pr.promo_price != null
|
||||
? parseFloat(String(pr.promo_price))
|
||||
: undefined,
|
||||
promo_percent: pr.promo_percent,
|
||||
})) || [],
|
||||
};
|
||||
setProduct(fixedProduct);
|
||||
if (fixedProduct.prices.length > 0) {
|
||||
setSelectedGrams(fixedProduct.prices[0].quantity);
|
||||
setSelectedPrice(fixedProduct.prices[0].price);
|
||||
const first = fixedProduct.prices[0];
|
||||
setSelectedGrams(first.quantity);
|
||||
setSelectedPrice(first.promo_price ?? first.price);
|
||||
}
|
||||
const matched = categories.find(
|
||||
(c) =>
|
||||
@@ -90,7 +96,7 @@ export default function ProductDetailScreen() {
|
||||
const handleGramsChange = (quantity: number) => {
|
||||
setSelectedGrams(quantity);
|
||||
const opt = product?.prices?.find((p) => p.quantity === quantity);
|
||||
if (opt) setSelectedPrice(opt.price);
|
||||
if (opt) setSelectedPrice(opt.promo_price ?? opt.price);
|
||||
setShowQuantityPicker(false);
|
||||
};
|
||||
|
||||
@@ -585,16 +591,45 @@ export default function ProductDetailScreen() {
|
||||
|
||||
<View style={styles.infoSection}>
|
||||
<Text style={styles.productName}>{product.name}</Text>
|
||||
{selectedPrice > 0 && (
|
||||
<View style={styles.priceRow}>
|
||||
<View style={styles.priceIndicator} />
|
||||
<Text style={styles.priceText}>
|
||||
{selectedPrice.toFixed(2)} €{" "}
|
||||
{selectedGrams &&
|
||||
`pour ${selectedGrams}${product.unit || "g"}`}
|
||||
</Text>
|
||||
</View>
|
||||
)}
|
||||
{selectedPrice > 0 && (() => {
|
||||
const selectedTier = product.prices?.find(
|
||||
(p) => p.quantity === selectedGrams,
|
||||
);
|
||||
const hasPromo =
|
||||
selectedTier?.promo_price != null &&
|
||||
selectedTier.promo_price < selectedTier.price;
|
||||
return (
|
||||
<View style={styles.priceRow}>
|
||||
<View style={styles.priceIndicator} />
|
||||
{hasPromo && (
|
||||
<Text
|
||||
style={[
|
||||
styles.priceText,
|
||||
{
|
||||
textDecorationLine: "line-through",
|
||||
opacity: 0.6,
|
||||
marginRight: 6,
|
||||
},
|
||||
]}
|
||||
>
|
||||
{selectedTier!.price.toFixed(2)} €
|
||||
</Text>
|
||||
)}
|
||||
<Text
|
||||
style={[
|
||||
styles.priceText,
|
||||
hasPromo && { color: "#22c55e" },
|
||||
]}
|
||||
>
|
||||
{selectedPrice.toFixed(2)} €{" "}
|
||||
{selectedGrams &&
|
||||
`pour ${selectedGrams}${product.unit || "g"}`}
|
||||
{hasPromo &&
|
||||
` (-${selectedTier!.promo_percent}%)`}
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
})()}
|
||||
<View style={styles.descriptionCard}>
|
||||
<Text style={styles.descriptionTitle}>Description</Text>
|
||||
<Text style={styles.descriptionText}>
|
||||
@@ -719,16 +754,32 @@ export default function ProductDetailScreen() {
|
||||
{p.quantity}
|
||||
{product.unit || "g"}
|
||||
</Text>
|
||||
<Text
|
||||
style={[
|
||||
styles.pickerOptionPrice,
|
||||
selectedGrams === p.quantity && {
|
||||
color: catColor,
|
||||
},
|
||||
]}
|
||||
>
|
||||
{p.price.toFixed(2)} €
|
||||
</Text>
|
||||
{p.promo_price != null && p.promo_price < p.price ? (
|
||||
<View style={{ flexDirection: "row", alignItems: "center", gap: 4 }}>
|
||||
<Text
|
||||
style={[
|
||||
styles.pickerOptionPrice,
|
||||
{ textDecorationLine: "line-through", opacity: 0.6 },
|
||||
]}
|
||||
>
|
||||
{p.price.toFixed(2)} €
|
||||
</Text>
|
||||
<Text style={[styles.pickerOptionPrice, { color: "#22c55e" }]}>
|
||||
{p.promo_price.toFixed(2)} € (-{p.promo_percent}%)
|
||||
</Text>
|
||||
</View>
|
||||
) : (
|
||||
<Text
|
||||
style={[
|
||||
styles.pickerOptionPrice,
|
||||
selectedGrams === p.quantity && {
|
||||
color: catColor,
|
||||
},
|
||||
]}
|
||||
>
|
||||
{p.price.toFixed(2)} €
|
||||
</Text>
|
||||
)}
|
||||
</View>
|
||||
{selectedGrams === p.quantity && (
|
||||
<View
|
||||
|
||||
@@ -34,7 +34,6 @@ const logoGrosSemi = require("../../../assets/logo-gros-semi.png");
|
||||
const { width: SCREEN_WIDTH } = Dimensions.get("window");
|
||||
const CARD_WIDTH = SCREEN_WIDTH - 48;
|
||||
|
||||
// Les catégories sont chargées dynamiquement depuis l'API
|
||||
|
||||
type Nav = NativeStackNavigationProp<ClientStackParamList>;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user