chore: build
Backend - Build & Lint / build (push) Failing after 16m27s
Frontend Admin - EAS Build / build (push) Failing after 2h15m57s

This commit is contained in:
Nuxgrid
2026-07-15 20:55:46 +02:00
parent e5333395b9
commit 74bf9cf14c
7 changed files with 400 additions and 1 deletions
+44
View File
@@ -416,6 +416,50 @@ export const getLivreurRatings = async (
}
};
export interface LoginHistoryEntry {
id: number;
username: string;
created_at: string;
}
export interface LoginHistoryWeek {
week: number;
entries: LoginHistoryEntry[];
}
export const getLivreurLoginHistory = async (
username: string,
year?: number,
month?: number,
): Promise<{
success: boolean;
year: number;
month: number;
weeks: LoginHistoryWeek[];
count: number;
}> => {
try {
const { data } = await apiClient.get(
`${V2}/admin/protected/delivery-persons/${username}/login-history`,
{ params: { year, month } },
);
return {
success: true,
year: data.year,
month: data.month,
weeks: data.weeks || [],
count: data.count || 0,
};
} catch {
return {
success: false,
year: year ?? new Date().getFullYear(),
month: month ?? new Date().getMonth() + 1,
weeks: [],
count: 0,
};
}
};
const parseStatus = (status: any): "available" | "busy" | "offline" => {
if (!status) return "offline";
if (status === "available" || status === "busy" || status === "offline")
@@ -34,8 +34,9 @@ import {
resetClientPoints,
addClientPoints,
subtractClientPoints,
getLivreurLoginHistory,
} from "../../api/api_admin";
import type { CancelledOrder } from "../../api/api_admin";
import type { CancelledOrder, LoginHistoryWeek } from "../../api/api_admin";
import type { ClientResponse } from "../../api/types";
import LoadingSpinner from "../../components/ui/LoadingSpinner";
import Card from "../../components/ui/Card";
@@ -225,6 +226,23 @@ export default function UsersScreen() {
loading: boolean;
}>({ visible: false, username: "", orders: [], loading: false });
// Login history modal (livreur)
const [loginHistoryModal, setLoginHistoryModal] = useState<{
visible: boolean;
username: string;
year: number;
month: number;
weeks: LoginHistoryWeek[];
loading: boolean;
}>({
visible: false,
username: "",
year: new Date().getFullYear(),
month: new Date().getMonth() + 1,
weeks: [],
loading: false,
});
const { alert, showError, showSuccess, showConfirm, hideAlert } =
useAlert();
@@ -708,6 +726,53 @@ export default function UsersScreen() {
}));
};
// --------------------------------------------------
// Login history (livreur)
// --------------------------------------------------
const fetchLoginHistory = async (
username: string,
year: number,
month: number,
) => {
setLoginHistoryModal((prev) => ({ ...prev, loading: true }));
const res = await getLivreurLoginHistory(username, year, month);
setLoginHistoryModal((prev) => ({
...prev,
year: res.year,
month: res.month,
weeks: res.weeks,
loading: false,
}));
};
const openLoginHistory = (username: string) => {
const now = new Date();
const year = now.getFullYear();
const month = now.getMonth() + 1;
setLoginHistoryModal({
visible: true,
username,
year,
month,
weeks: [],
loading: true,
});
fetchLoginHistory(username, year, month);
};
const changeLoginHistoryMonth = (delta: number) => {
let year = loginHistoryModal.year;
let month = loginHistoryModal.month + delta;
if (month < 1) {
month = 12;
year -= 1;
} else if (month > 12) {
month = 1;
year += 1;
}
fetchLoginHistory(loginHistoryModal.username, year, month);
};
// --------------------------------------------------
// Create user
// --------------------------------------------------
@@ -1150,6 +1215,18 @@ export default function UsersScreen() {
</TouchableOpacity>
</>
)}
{item.role === "livreur" && (
<TouchableOpacity
style={styles.editIconBtn}
onPress={() => openLoginHistory(item.username)}
>
<Ionicons
name="time-outline"
size={20}
color={colors.accent}
/>
</TouchableOpacity>
)}
{(item.role === "livreur" ||
item.role === "cabine") && (
<TouchableOpacity
@@ -1799,6 +1876,153 @@ export default function UsersScreen() {
)}
</Modal>
{/* Modal historique de connexion (livreur) */}
<Modal
visible={loginHistoryModal.visible}
onClose={() =>
setLoginHistoryModal((prev) => ({
...prev,
visible: false,
}))
}
title={`Connexions — ${loginHistoryModal.username}`}
icon="time-outline"
iconColor={colors.accent}
>
<View
style={{
flexDirection: "row",
alignItems: "center",
justifyContent: "space-between",
marginBottom: spacing.m,
}}
>
<TouchableOpacity
style={styles.editIconBtn}
onPress={() => changeLoginHistoryMonth(-1)}
>
<Ionicons
name="chevron-back"
size={20}
color={colors.textWhite}
/>
</TouchableOpacity>
<Text
style={{
color: colors.textWhite,
fontWeight: "700",
fontSize: fontSize.md,
}}
>
{new Date(
loginHistoryModal.year,
loginHistoryModal.month - 1,
1,
)
.toLocaleDateString("fr-FR", {
month: "long",
year: "numeric",
})
.replace(/^./, (c) => c.toUpperCase())}
</Text>
<TouchableOpacity
style={styles.editIconBtn}
onPress={() => changeLoginHistoryMonth(1)}
>
<Ionicons
name="chevron-forward"
size={20}
color={colors.textWhite}
/>
</TouchableOpacity>
</View>
{loginHistoryModal.loading ? (
<ActivityIndicator
color={colors.accent}
style={{ marginVertical: spacing.xl }}
/>
) : loginHistoryModal.weeks.length === 0 ? (
<Text
style={{
color: colors.textMuted,
textAlign: "center",
paddingVertical: spacing.xl,
}}
>
Aucune connexion ce mois-ci
</Text>
) : (
<ScrollView
style={{ maxHeight: 400 }}
showsVerticalScrollIndicator={false}
>
{loginHistoryModal.weeks.map((week) => (
<View
key={week.week}
style={{ marginBottom: spacing.m }}
>
<Text
style={{
color: colors.textMuted,
fontSize: fontSize.xs,
fontWeight: "700",
textTransform: "uppercase",
marginBottom: spacing.xs,
}}
>
Semaine {week.week}
</Text>
{week.entries.map((entry, idx) => (
<View
key={entry.id}
style={{
flexDirection: "row",
justifyContent: "space-between",
paddingVertical: spacing.xs,
borderBottomWidth:
idx <
week.entries.length - 1
? 1
: 0,
borderBottomColor: colors.border,
}}
>
<Text
style={{
color: colors.textWhite,
fontSize: fontSize.sm,
}}
>
{new Date(
entry.created_at,
).toLocaleDateString("fr-FR", {
weekday: "short",
day: "2-digit",
month: "2-digit",
})}
</Text>
<Text
style={{
color: colors.textMuted,
fontSize: fontSize.sm,
}}
>
{new Date(
entry.created_at,
).toLocaleTimeString("fr-FR", {
hour: "2-digit",
minute: "2-digit",
})}
</Text>
</View>
))}
</View>
))}
</ScrollView>
)}
</Modal>
{/* Modal points & amendes */}
<Modal
visible={sanctionModal.visible}