chore: add parrainage
This commit is contained in:
@@ -16,7 +16,10 @@ import {
|
||||
getAllClients,
|
||||
getAvailableDeliveryPersons,
|
||||
getCommandCountByStatus,
|
||||
registerAdminPushToken,
|
||||
unregisterAdminPushToken,
|
||||
} from "../../api/api_admin";
|
||||
import { getExpoPushToken } from "../../utils/pushTokenUtils";
|
||||
import LoadingSpinner from "../../components/ui/LoadingSpinner";
|
||||
|
||||
interface StatCard {
|
||||
@@ -33,6 +36,14 @@ export default function DashboardScreen() {
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [refreshing, setRefreshing] = useState(false);
|
||||
|
||||
// Enregistrement push token admin au montage
|
||||
useEffect(() => {
|
||||
getExpoPushToken().then((token) => {
|
||||
if (token) registerAdminPushToken(token);
|
||||
});
|
||||
return () => { unregisterAdminPushToken(); };
|
||||
}, []);
|
||||
|
||||
const loadStats = useCallback(async () => {
|
||||
try {
|
||||
const [allCmd, pending, enRoute, completed, clients, livreurs] =
|
||||
|
||||
@@ -149,6 +149,7 @@ export default function SettingsScreen() {
|
||||
points_separated: true,
|
||||
points_weed_tiers: [],
|
||||
points_zipette_tiers: [],
|
||||
referral_enabled: true,
|
||||
});
|
||||
const [categories, setCategories] = useState<Category[]>([]);
|
||||
|
||||
@@ -350,6 +351,28 @@ export default function SettingsScreen() {
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* Parrainage */}
|
||||
<View style={s.section}>
|
||||
<Text style={s.sectionTitle}>Parrainage</Text>
|
||||
<View style={[s.row, s.rowFirst]}>
|
||||
<View style={s.rowLeft}>
|
||||
<Text style={s.rowLabel}>Parrainage activé</Text>
|
||||
<Text style={s.rowDesc}>
|
||||
Les clients peuvent voir et utiliser leur solde parrainage au checkout.{"\n"}
|
||||
Désactivé : le solde reste en base mais ne peut plus être utilisé.
|
||||
</Text>
|
||||
</View>
|
||||
<Switch
|
||||
value={settings.referral_enabled}
|
||||
onValueChange={(v) =>
|
||||
setSettings((prev) => ({ ...prev, referral_enabled: v }))
|
||||
}
|
||||
trackColor={{ false: colors.border, true: colors.accent }}
|
||||
thumbColor="#fff"
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* Système de points */}
|
||||
<View style={s.section}>
|
||||
<Text style={s.sectionTitle}>Système de points</Text>
|
||||
|
||||
@@ -19,6 +19,7 @@ import {
|
||||
deleteClientAdmin,
|
||||
createClientByAdmin,
|
||||
createUserByAdmin,
|
||||
creditClientReferral,
|
||||
} from "../../api/api_admin";
|
||||
import type { ClientResponse } from "../../api/types";
|
||||
import LoadingSpinner from "../../components/ui/LoadingSpinner";
|
||||
@@ -155,6 +156,15 @@ export default function UsersScreen() {
|
||||
const [createTel, setCreateTel] = useState("");
|
||||
const [creating, setCreating] = useState(false);
|
||||
|
||||
// Referral credit modal
|
||||
const [referralModal, setReferralModal] = useState<{
|
||||
visible: boolean;
|
||||
username: string;
|
||||
currentBalance: number;
|
||||
}>({ visible: false, username: "", currentBalance: 0 });
|
||||
const [referralAmount, setReferralAmount] = useState("");
|
||||
const [referralLoading, setReferralLoading] = useState(false);
|
||||
|
||||
const { alert, showError, showConfirm, hideAlert } = useAlert();
|
||||
|
||||
// --------------------------------------------------
|
||||
@@ -318,6 +328,40 @@ export default function UsersScreen() {
|
||||
);
|
||||
};
|
||||
|
||||
// --------------------------------------------------
|
||||
// Referral credit
|
||||
// --------------------------------------------------
|
||||
const openReferralModal = (client: ClientResponse) => {
|
||||
setReferralAmount("");
|
||||
setReferralModal({
|
||||
visible: true,
|
||||
username: client.username,
|
||||
currentBalance: client.referral_balance ?? 0,
|
||||
});
|
||||
};
|
||||
|
||||
const handleCreditReferral = async () => {
|
||||
const amount = parseFloat(referralAmount);
|
||||
if (isNaN(amount) || amount <= 0) {
|
||||
showError("Erreur", "Entrez un montant valide");
|
||||
return;
|
||||
}
|
||||
setReferralLoading(true);
|
||||
try {
|
||||
const res = await creditClientReferral(referralModal.username, amount);
|
||||
if (res.success) {
|
||||
setReferralModal((prev) => ({ ...prev, visible: false }));
|
||||
await loadData();
|
||||
} else {
|
||||
showError("Erreur", res.message || "Echec du crédit");
|
||||
}
|
||||
} catch (e: any) {
|
||||
showError("Erreur", e.message);
|
||||
} finally {
|
||||
setReferralLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
// --------------------------------------------------
|
||||
// Create user
|
||||
// --------------------------------------------------
|
||||
@@ -603,16 +647,28 @@ export default function UsersScreen() {
|
||||
</View>
|
||||
<View style={{ flexDirection: "row", gap: spacing.xs }}>
|
||||
{isClient && (
|
||||
<TouchableOpacity
|
||||
style={styles.editIconBtn}
|
||||
onPress={() => openEditClient(item.clientData!)}
|
||||
>
|
||||
<Ionicons
|
||||
name="create-outline"
|
||||
size={20}
|
||||
color={colors.info}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
<>
|
||||
<TouchableOpacity
|
||||
style={styles.editIconBtn}
|
||||
onPress={() => openReferralModal(item.clientData!)}
|
||||
>
|
||||
<Ionicons
|
||||
name="gift-outline"
|
||||
size={20}
|
||||
color={colors.success}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity
|
||||
style={styles.editIconBtn}
|
||||
onPress={() => openEditClient(item.clientData!)}
|
||||
>
|
||||
<Ionicons
|
||||
name="create-outline"
|
||||
size={20}
|
||||
color={colors.info}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
</>
|
||||
)}
|
||||
{(item.role === "livreur" ||
|
||||
item.role === "cabine") && (
|
||||
@@ -704,6 +760,19 @@ export default function UsersScreen() {
|
||||
</Text>
|
||||
<Text style={styles.statLabel}>Annul.</Text>
|
||||
</View>
|
||||
{(item.clientData.referral_balance ?? 0) > 0 && (
|
||||
<View style={styles.stat}>
|
||||
<Text
|
||||
style={[
|
||||
styles.statValue,
|
||||
{ color: colors.success },
|
||||
]}
|
||||
>
|
||||
{(item.clientData.referral_balance ?? 0).toFixed(0)}€
|
||||
</Text>
|
||||
<Text style={styles.statLabel}>Parrain</Text>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
</>
|
||||
)}
|
||||
@@ -977,6 +1046,35 @@ export default function UsersScreen() {
|
||||
confirmText={alert.confirmText}
|
||||
cancelText={alert.cancelText}
|
||||
/>
|
||||
|
||||
{/* Modal crédit parrainage */}
|
||||
<Modal
|
||||
visible={referralModal.visible}
|
||||
onClose={() => setReferralModal((prev) => ({ ...prev, visible: false }))}
|
||||
title={`Crédit parrainage — ${referralModal.username}`}
|
||||
icon="gift-outline"
|
||||
iconColor={colors.success}
|
||||
>
|
||||
<Text style={{ color: colors.textSecondary, fontSize: fontSize.sm, marginBottom: spacing.s }}>
|
||||
Solde actuel : {referralModal.currentBalance.toFixed(2)} €
|
||||
</Text>
|
||||
<TextInput
|
||||
placeholder="Montant à créditer (ex: 10)"
|
||||
value={referralAmount}
|
||||
onChangeText={setReferralAmount}
|
||||
keyboardType="decimal-pad"
|
||||
icon={<Ionicons name="cash-outline" size={18} color={colors.textMuted} />}
|
||||
/>
|
||||
<Button
|
||||
title={referralLoading ? "Chargement..." : "Créditer"}
|
||||
onPress={handleCreditReferral}
|
||||
disabled={referralLoading}
|
||||
variant="success"
|
||||
size="md"
|
||||
fullWidth
|
||||
style={{ marginTop: spacing.m }}
|
||||
/>
|
||||
</Modal>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -12,12 +12,21 @@ import { useTheme } from "../../context/ThemeContext";
|
||||
import { shadows } from "../../theme/shadows";
|
||||
import { useAuth } from "../../auth/AuthContext";
|
||||
import { getAllCommands } from "../../api/api_admin";
|
||||
import { getAllDeliveryPersonsWithDetails } from "../../api/api_cabine";
|
||||
import { getAllDeliveryPersonsWithDetails, registerCabinePushToken, unregisterCabinePushToken } from "../../api/api_cabine";
|
||||
import { getExpoPushToken } from "../../utils/pushTokenUtils";
|
||||
import LoadingSpinner from "../../components/ui/LoadingSpinner";
|
||||
|
||||
export default function DashboardScreen() {
|
||||
const { colors } = useTheme();
|
||||
const { username } = useAuth();
|
||||
|
||||
// Enregistrement push token cabine au montage
|
||||
useEffect(() => {
|
||||
getExpoPushToken().then((token) => {
|
||||
if (token) registerCabinePushToken(token);
|
||||
});
|
||||
return () => { unregisterCabinePushToken(); };
|
||||
}, []);
|
||||
const [stats, setStats] = useState<
|
||||
Array<{
|
||||
label: string;
|
||||
|
||||
Reference in New Issue
Block a user