chore: add comming soon category
This commit is contained in:
@@ -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>
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
Reference in New Issue
Block a user