chore: build
This commit is contained in:
@@ -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}
|
||||
|
||||
Reference in New Issue
Block a user