chore: add comming soon category

This commit is contained in:
2026-03-07 17:59:34 +01:00
parent 0eb775f3ef
commit ab7d8929a8
4 changed files with 81 additions and 6 deletions
+5 -2
View File
@@ -591,6 +591,7 @@ export interface Category {
id: number; id: number;
name: string; name: string;
color: string; color: string;
is_coming_soon: boolean;
created_at: string; created_at: string;
} }
@@ -606,11 +607,12 @@ export const getCategories = async (): Promise<Category[]> => {
export const createCategoryAdmin = async ( export const createCategoryAdmin = async (
name: string, name: string,
color: string, color: string,
isComingSoon: boolean = false,
): Promise<{ success: boolean; category?: Category; error?: string }> => { ): Promise<{ success: boolean; category?: Category; error?: string }> => {
try { try {
const { data } = await apiClient.post( const { data } = await apiClient.post(
`${V2}/admin/protected/categories`, `${V2}/admin/protected/categories`,
{ name, color }, { name, color, is_coming_soon: isComingSoon },
); );
return { success: true, category: data.category }; return { success: true, category: data.category };
} catch (error: any) { } catch (error: any) {
@@ -625,11 +627,12 @@ export const updateCategoryAdmin = async (
id: number, id: number,
name: string, name: string,
color: string, color: string,
isComingSoon: boolean = false,
): Promise<{ success: boolean; category?: Category; error?: string }> => { ): Promise<{ success: boolean; category?: Category; error?: string }> => {
try { try {
const { data } = await apiClient.put( const { data } = await apiClient.put(
`${V2}/admin/protected/categories/${id}`, `${V2}/admin/protected/categories/${id}`,
{ name, color }, { name, color, is_coming_soon: isComingSoon },
); );
return { success: true, category: data.category }; return { success: true, category: data.category };
} catch (error: any) { } catch (error: any) {
@@ -10,6 +10,7 @@ import {
KeyboardAvoidingView, KeyboardAvoidingView,
Platform, Platform,
ScrollView, ScrollView,
Switch,
} from "react-native"; } from "react-native";
import { Ionicons } from "@expo/vector-icons"; import { Ionicons } from "@expo/vector-icons";
import { spacing, fontSize, borderRadius } from "../../theme"; import { spacing, fontSize, borderRadius } from "../../theme";
@@ -44,6 +45,7 @@ export default function CategoriesScreen() {
const [name, setName] = useState(""); const [name, setName] = useState("");
const [color, setColor] = useState("#7c3aed"); const [color, setColor] = useState("#7c3aed");
const [hexInput, setHexInput] = useState("#7c3aed"); const [hexInput, setHexInput] = useState("#7c3aed");
const [isComingSoon, setIsComingSoon] = useState(false);
const [saving, setSaving] = useState(false); const [saving, setSaving] = useState(false);
const { alert, showError, showSuccess, showConfirm, hideAlert } = useAlert(); const { alert, showError, showSuccess, showConfirm, hideAlert } = useAlert();
@@ -74,6 +76,7 @@ export default function CategoriesScreen() {
setEditingCategory(null); setEditingCategory(null);
setName(""); setName("");
selectColor("#7c3aed"); selectColor("#7c3aed");
setIsComingSoon(false);
setModalVisible(true); setModalVisible(true);
}; };
@@ -81,12 +84,14 @@ export default function CategoriesScreen() {
setEditingCategory(cat); setEditingCategory(cat);
setName(cat.name); setName(cat.name);
selectColor(cat.color || "#7c3aed"); selectColor(cat.color || "#7c3aed");
setIsComingSoon(cat.is_coming_soon || false);
setModalVisible(true); setModalVisible(true);
}; };
const closeModal = () => { const closeModal = () => {
setModalVisible(false); setModalVisible(false);
setName(""); setName("");
setIsComingSoon(false);
setEditingCategory(null); setEditingCategory(null);
}; };
@@ -102,7 +107,7 @@ export default function CategoriesScreen() {
} }
setSaving(true); setSaving(true);
if (editingCategory) { if (editingCategory) {
const res = await updateCategoryAdmin(editingCategory.id, trimmed, color); const res = await updateCategoryAdmin(editingCategory.id, trimmed, color, isComingSoon);
if (res.success) { if (res.success) {
showSuccess("Succès", "Catégorie modifiée"); showSuccess("Succès", "Catégorie modifiée");
closeModal(); closeModal();
@@ -111,7 +116,7 @@ export default function CategoriesScreen() {
showError("Erreur", res.error || "Erreur"); showError("Erreur", res.error || "Erreur");
} }
} else { } else {
const res = await createCategoryAdmin(trimmed, color); const res = await createCategoryAdmin(trimmed, color, isComingSoon);
if (res.success) { if (res.success) {
showSuccess("Succès", "Catégorie créée"); showSuccess("Succès", "Catégorie créée");
closeModal(); closeModal();
@@ -175,6 +180,15 @@ export default function CategoriesScreen() {
textTransform: "capitalize", textTransform: "capitalize",
}, },
catDate: { fontSize: fontSize.xs, color: colors.textSecondary, marginTop: 2 }, catDate: { fontSize: fontSize.xs, color: colors.textSecondary, marginTop: 2 },
comingSoonBadge: {
backgroundColor: colors.warning + "33",
borderWidth: 1,
borderColor: colors.warning,
borderRadius: borderRadius.sm,
paddingHorizontal: 6,
paddingVertical: 2,
},
comingSoonBadgeText: { fontSize: fontSize.xs, color: colors.warning, fontWeight: "600" },
actions: { flexDirection: "row", gap: spacing.s }, actions: { flexDirection: "row", gap: spacing.s },
actionBtn: { padding: 8 }, actionBtn: { padding: 8 },
overlay: { overlay: {
@@ -230,6 +244,15 @@ export default function CategoriesScreen() {
borderRadius: borderRadius.sm, borderRadius: borderRadius.sm,
borderWidth: 2, borderWidth: 2,
}, },
comingSoonRow: {
flexDirection: "row",
alignItems: "center",
justifyContent: "space-between",
paddingVertical: spacing.s,
borderTopWidth: 1,
borderTopColor: colors.border,
},
comingSoonHint: { fontSize: fontSize.xs, color: colors.textMuted, marginTop: 2 },
modalBtns: { flexDirection: "row", gap: spacing.s }, modalBtns: { flexDirection: "row", gap: spacing.s },
cancelBtn: { cancelBtn: {
flex: 1, flex: 1,
@@ -285,7 +308,14 @@ export default function CategoriesScreen() {
]} ]}
/> />
<View> <View>
<Text style={styles.catName}>{item.name}</Text> <View style={{ flexDirection: "row", alignItems: "center", gap: 6 }}>
<Text style={styles.catName}>{item.name}</Text>
{item.is_coming_soon && (
<View style={styles.comingSoonBadge}>
<Text style={styles.comingSoonBadgeText}>Prochainement</Text>
</View>
)}
</View>
<Text style={styles.catDate}> <Text style={styles.catDate}>
Créée le{" "} Créée le{" "}
{new Date(item.created_at).toLocaleDateString("fr-FR")} {new Date(item.created_at).toLocaleDateString("fr-FR")}
@@ -378,6 +408,21 @@ export default function CategoriesScreen() {
</View> </View>
</View> </View>
<View style={styles.comingSoonRow}>
<View>
<Text style={styles.label}>Prochainement</Text>
<Text style={styles.comingSoonHint}>
Affiche un message d'attente aux clients
</Text>
</View>
<Switch
value={isComingSoon}
onValueChange={setIsComingSoon}
trackColor={{ false: colors.border, true: colors.accent }}
thumbColor={colors.textWhite}
/>
</View>
<View style={styles.modalBtns}> <View style={styles.modalBtns}>
<TouchableOpacity style={styles.cancelBtn} onPress={closeModal}> <TouchableOpacity style={styles.cancelBtn} onPress={closeModal}>
<Text style={styles.cancelBtnText}>Annuler</Text> <Text style={styles.cancelBtnText}>Annuler</Text>
+1
View File
@@ -117,6 +117,7 @@ export interface Category {
id: number; id: number;
name: string; name: string;
color: string; color: string;
is_coming_soon: boolean;
created_at: string; created_at: string;
} }
+27 -1
View File
@@ -164,6 +164,13 @@ export default function ProductsScreen() {
paddingBottom: 96, paddingBottom: 96,
pointerEvents: "none", pointerEvents: "none",
}, },
comingSoonContent: {
flex: 1,
justifyContent: "center",
alignItems: "center",
padding: spacing.xl,
gap: spacing.m,
},
comingSoonText: { comingSoonText: {
fontSize: 36, fontSize: 36,
color: "#8E8FE8", color: "#8E8FE8",
@@ -174,10 +181,18 @@ export default function ProductsScreen() {
textShadowOffset: { width: 0, height: 0 }, textShadowOffset: { width: 0, height: 0 },
textShadowRadius: 18, textShadowRadius: 18,
}, },
comingSoonDesc: {
fontSize: fontSize.md,
color: "rgba(142, 143, 232, 0.7)",
textAlign: "center",
},
}), }),
[colors], [colors],
); );
const selectedCategoryObj = categories.find((c) => c.name === selectedCategory);
const isSelectedComingSoon = selectedCategoryObj?.is_coming_soon ?? false;
if (loading && !refreshing) { if (loading && !refreshing) {
return <LoadingSpinner message="Chargement des produits..." />; return <LoadingSpinner message="Chargement des produits..." />;
} }
@@ -215,7 +230,18 @@ export default function ProductsScreen() {
{selectedCategory === "tous" ? "Tous les produits" : selectedCategory} {selectedCategory === "tous" ? "Tous les produits" : selectedCategory}
</Text> </Text>
</View> </View>
{error ? ( {isSelectedComingSoon ? (
<View style={styles.comingSoonContent}>
<Animated.Text
style={[styles.comingSoonText, { transform: [{ scale: scaleAnim }] }]}
>
Prochainement
</Animated.Text>
<Text style={styles.comingSoonDesc}>
Les produits de cette catégorie arrivent bientôt !
</Text>
</View>
) : error ? (
<View style={styles.center}> <View style={styles.center}>
<Text style={styles.errorText}>{error}</Text> <Text style={styles.errorText}>{error}</Text>
</View> </View>