freshyo/apps/admin-ui/app/(drawer)/product-groupings/index.tsx
2026-01-24 00:13:15 +05:30

254 lines
7.2 KiB
TypeScript

import React, { useState } from "react";
import { View, ScrollView, TouchableOpacity, Alert } from "react-native";
import {
theme,
AppContainer,
MyText,
tw,
useManualRefresh,
useMarkDataFetchers,
MyTouchableOpacity,
BottomDialog,
} from "common-ui";
import { trpc } from "../../../src/trpc-client";
import MaterialIcons from "@expo/vector-icons/MaterialIcons";
import { LinearGradient } from "expo-linear-gradient";
import dayjs from "dayjs";
import { useRouter } from "expo-router";
interface ProductGroup {
id: number;
groupName: string;
description: string | null;
createdAt: string;
products: any[];
productCount: number;
}
const GroupItem = ({
group,
onEdit,
onDelete,
onViewProducts,
index,
}: {
group: ProductGroup;
onEdit: (group: ProductGroup) => void;
onDelete: (id: number) => void;
onViewProducts: (products: any[]) => void;
index: number;
}) => {
return (
<View style={tw``}>
<View style={tw`px-2 py-6`}>
{/* Top Header: Name & Actions */}
<View style={tw`flex-row justify-between items-start mb-6`}>
<View style={tw`flex-row items-center flex-1`}>
<View
style={tw`w-12 h-12 rounded-2xl bg-brand50 items-center justify-center mr-4`}
>
<MaterialIcons
name="group"
size={24}
color={theme.colors.brand600}
/>
</View>
<View style={tw`flex-1`}>
<MyText
style={tw`text-slate-400 text-[10px] font-black uppercase tracking-widest`}
>
Group Name
</MyText>
<MyText
style={tw`text-xl font-black text-slate-900`}
numberOfLines={1}
>
{group.groupName}
</MyText>
</View>
</View>
<View style={tw`flex-row items-center`}>
<TouchableOpacity
onPress={() => onEdit(group)}
style={tw`p-2 mr-2`}
>
<MaterialIcons name="edit" size={20} color="#64748B" />
</TouchableOpacity>
<TouchableOpacity
onPress={() => onDelete(group.id)}
style={tw`p-2`}
>
<MaterialIcons name="delete" size={20} color="#EF4444" />
</TouchableOpacity>
</View>
</View>
{/* Description */}
{group.description && (
<View
style={tw`bg-slate-50 rounded-2xl p-4 mb-6 border border-slate-100`}
>
<MyText style={tw`text-slate-700 font-medium`}>
{group.description}
</MyText>
</View>
)}
{/* Stats */}
<View style={tw`flex-row items-center justify-between`}>
<View style={tw`flex-row items-center`}>
<TouchableOpacity
onPress={() => onViewProducts(group.products)}
style={tw`flex-row items-center mr-4`}
>
<MaterialIcons
name="inventory"
size={14}
color={theme.colors.brand500}
/>
<MyText style={tw`text-xs font-bold text-brand500 ml-1.5`}>
{group.productCount} Products
</MyText>
</TouchableOpacity>
<View style={tw`flex-row items-center`}>
<MaterialIcons name="schedule" size={14} color="#94A3B8" />
<MyText style={tw`text-xs font-bold text-slate-500 ml-1.5`}>
{dayjs(group.createdAt).format("MMM DD, YYYY")}
</MyText>
</View>
</View>
</View>
</View>
</View>
);
};
export default function ProductGroupings() {
const router = useRouter();
const {
data: groupsData,
isLoading,
error,
refetch,
} = trpc.admin.product.getGroups.useQuery();
const deleteGroup = trpc.admin.product.deleteGroup.useMutation();
const groups = groupsData?.groups || [];
const [viewProducts, setViewProducts] = useState<any[] | null>(null);
useManualRefresh(refetch);
useMarkDataFetchers(() => {
refetch();
});
const handleCreate = () => {
router.push("/(drawer)/create-product-group");
};
const handleEdit = (group: ProductGroup) => {
router.push(`/(drawer)/edit-product-group/${group.id}`);
};
const handleDelete = (id: number) => {
Alert.alert("Delete Group", "Are you sure you want to delete this group?", [
{ text: "Cancel", style: "cancel" },
{
text: "Delete",
style: "destructive",
onPress: () => {
deleteGroup.mutate(
{ id },
{
onSuccess: () => {
refetch();
},
}
);
},
},
]);
};
if (isLoading) {
return (
<AppContainer>
<View style={tw`flex-1 justify-center items-center`}>
<MyText style={tw`text-gray-600`}>Loading product groups...</MyText>
</View>
</AppContainer>
);
}
if (error) {
return (
<AppContainer>
<View style={tw`flex-1 justify-center items-center`}>
<MyText style={tw`text-red-600`}>Error loading groups</MyText>
</View>
</AppContainer>
);
}
return (
<View style={tw`flex-1 relative px-4 bg-white`}>
<ScrollView
style={[tw`flex-1`]}
contentContainerStyle={tw`pb-32`}
showsVerticalScrollIndicator={false}
bounces={false}
>
{groups.length === 0 ? (
<View style={tw`flex-1 justify-center items-center py-8`}>
<View
style={tw`w-24 h-24 bg-slate-50 rounded-full items-center justify-center mb-6`}
>
<MaterialIcons name="group-work" size={48} color="#94A3B8" />
</View>
<MyText
style={tw`text-slate-900 text-xl font-black tracking-tight`}
>
No Groups Yet
</MyText>
<MyText
style={tw`text-slate-500 text-center mt-2 font-medium px-8`}
>
Start by creating your first product group using the button below.
</MyText>
</View>
) : (
groups.map((group, index) => (
<React.Fragment key={group.id}>
<GroupItem
group={group}
index={index}
onEdit={handleEdit}
onDelete={handleDelete}
onViewProducts={setViewProducts}
/>
{index < groups.length - 1 && (
<View style={tw`h-px bg-slate-200 w-full`} />
)}
</React.Fragment>
))
)}
{/* Global Floating Action Button */}
</ScrollView>
<MyTouchableOpacity
onPress={handleCreate}
activeOpacity={0.95}
style={{ position: "absolute", bottom: 32, right: 24, zIndex: 100 }}
>
<LinearGradient
colors={["#1570EF", "#194185"]}
start={{ x: 0, y: 0 }}
end={{ x: 1, y: 1 }}
style={tw`w-16 h-16 rounded-[24px] items-center justify-center shadow-lg shadow-brand300`}
>
<MaterialIcons name="add" size={32} color="white" />
</LinearGradient>
</MyTouchableOpacity>
</View>
);
}