freshyo/apps/admin-ui/app/(drawer)/customize-app/index.tsx
2026-04-11 12:04:27 +05:30

276 lines
8.9 KiB
TypeScript

import React from 'react';
import { View, ScrollView, Alert } from 'react-native';
import { Formik } from 'formik';
import { MyText, MyTextInput, MyTouchableOpacity, tw, AppContainer, Checkbox } from 'common-ui';
import { trpc } from '../../../src/trpc-client';
import { useRouter } from 'expo-router';
import MaterialIcons from '@expo/vector-icons/MaterialIcons';
type ConstantFormData = Record<string, any>
const CONST_LABELS: Record<string, string> = {
minRegularOrderValue: 'Minimum Regular Order Value',
freeDeliveryThreshold: 'Free Delivery Threshold',
deliveryCharge: 'Delivery Charge',
flashFreeDeliveryThreshold: 'Flash Free Delivery Threshold',
flashDeliveryCharge: 'Flash Delivery Charge',
platformFeePercent: 'Platform Fee Percent',
taxRate: 'Tax Rate',
minOrderAmountForCoupon: 'Minimum Order Amount for Coupon',
maxCouponDiscount: 'Maximum Coupon Discount',
flashDeliverySlotId: 'Flash Delivery Slot ID',
readableOrderId: 'Readable Order ID',
versionNum: 'Version Number',
playStoreUrl: 'Play Store URL',
appStoreUrl: 'App Store URL',
popularItems: 'Popular Items',
allItemsOrder: 'All Items Order',
isFlashDeliveryEnabled: 'Enable Flash Delivery',
supportMobile: 'Support Mobile',
supportEmail: 'Support Email',
};
const CONST_VISIBILITY: Record<string, boolean> = {
minRegularOrderValue: true,
freeDeliveryThreshold: true,
deliveryCharge: true,
flashFreeDeliveryThreshold: true,
flashDeliveryCharge: true,
platformFeePercent: true,
taxRate: false,
minOrderAmountForCoupon: true,
maxCouponDiscount: false,
flashDeliverySlotId: true,
readableOrderId: false,
versionNum: true,
playStoreUrl: true,
appStoreUrl: true,
popularItems: true,
allItemsOrder: true,
isFlashDeliveryEnabled: true,
supportMobile: true,
supportEmail: true,
tester: false,
};
interface ConstantInputProps {
constantKey: string;
value: any;
setFieldValue: (field: string, value: any) => void;
router: any;
}
const ConstantInput: React.FC<ConstantInputProps> = ({ constantKey, value, setFieldValue, router }) => {
const fieldName = constantKey
// Special handling for popularItems - show navigation button instead of input
if (constantKey === 'popularItems') {
return (
<View>
<MyText style={tw`text-sm font-medium text-gray-700 mb-2`}>
{CONST_LABELS[constantKey] || constantKey}
</MyText>
<MyTouchableOpacity
onPress={() => router.push('/(drawer)/customize-app/popular-items')}
style={tw`bg-blue-50 border-2 border-dashed border-blue-200 p-4 rounded-lg flex-row items-center justify-center`}
>
<MaterialIcons name="edit" size={20} color="#3b82f6" style={tw`mr-2`} />
<MyText style={tw`text-blue-700 font-medium`}>
Manage Popular Items ({Array.isArray(value) ? value.length : 0} items)
</MyText>
<MaterialIcons name="chevron-right" size={20} color="#3b82f6" style={tw`ml-2`} />
</MyTouchableOpacity>
</View>
);
}
// Special handling for allItemsOrder - show navigation button instead of input
if (constantKey === 'allItemsOrder') {
return (
<View>
<MyText style={tw`text-sm font-medium text-gray-700 mb-2`}>
{CONST_LABELS[constantKey] || constantKey}
</MyText>
<MyTouchableOpacity
onPress={() => router.push('/(drawer)/customize-app/all-items-order')}
style={tw`bg-green-50 border-2 border-dashed border-green-200 p-4 rounded-lg flex-row items-center justify-center`}
>
<MaterialIcons name="reorder" size={20} color="#16a34a" style={tw`mr-2`} />
<MyText style={tw`text-green-700 font-medium`}>
Manage All Visible Items ({Array.isArray(value) ? value.length : 0} items)
</MyText>
<MaterialIcons name="chevron-right" size={20} color="#16a34a" style={tw`ml-2`} />
</MyTouchableOpacity>
</View>
);
}
// Handle boolean values - show checkbox
if (typeof value === 'boolean') {
return (
<View>
<MyText style={tw`text-sm font-medium text-gray-700 mb-2`}>
{CONST_LABELS[constantKey] || constantKey}
</MyText>
<View style={tw`flex-row items-center`}>
<Checkbox
checked={value}
onPress={() => setFieldValue(fieldName, !value)}
size={28}
/>
<MyText style={tw`ml-3 text-gray-700`}>
{value ? 'Enabled' : 'Disabled'}
</MyText>
</View>
</View>
);
}
// Handle different value types
if (typeof value === 'number') {
return (
<MyTextInput
topLabel={CONST_LABELS[constantKey] || constantKey}
value={value.toString()}
onChangeText={(value) => {
const numValue = parseFloat(value);
setFieldValue(fieldName, isNaN(numValue) ? 0 : numValue);
}}
keyboardType="numeric"
placeholder="Enter number"
/>
);
}
if (Array.isArray(value)) {
return (
<MyTextInput
topLabel={CONST_LABELS[constantKey] || constantKey}
value={value.join(', ')}
onChangeText={(value) => {
const arrayValue = value.split(',').map(s => s.trim()).filter(s => s.length > 0);
setFieldValue(fieldName, arrayValue);
}}
placeholder="Enter comma separated values"
/>
);
}
// Default to string
return (
<MyTextInput
topLabel={CONST_LABELS[constantKey] || constantKey}
// value={value === null || value === undefined ? '' : String(value)}
value={value}
onChangeText={(value) => {
setFieldValue(fieldName, value)
}}
placeholder="Enter value"
/>
);
};
export default function CustomizeApp() {
const router = useRouter();
const { data: constants, isLoading: isLoadingConstants, refetch } = trpc.admin.const.getConstants.useQuery();
const { mutate: updateConstants, isPending: isUpdating } = trpc.admin.const.updateConstants.useMutation();
const handleSubmit = (values: ConstantFormData) => {
// Filter out constants that haven't changed
const changedConstants = (constants || []).filter((constant) => {
const nextValue = values[constant.key]
return JSON.stringify(nextValue) !== JSON.stringify(constant.value)
}).map((constant) => ({
key: constant.key,
value: values[constant.key],
}))
if (changedConstants.length === 0) {
Alert.alert('No Changes', 'No constants were modified.');
return;
}
updateConstants(
{ constants: changedConstants },
{
onSuccess: (response) => {
Alert.alert('Success', `Updated ${response.updatedCount} constants successfully.`);
refetch();
},
onError: (error) => {
Alert.alert('Error', 'Failed to update constants. Please try again.');
console.error('Update constants error:', error);
},
}
);
};
if (isLoadingConstants) {
return (
<View style={tw`flex-1 justify-center items-center`}>
<MyText style={tw`text-gray-500`}>Loading constants...</MyText>
</View>
);
}
if (!constants) {
return (
<View style={tw`flex-1 justify-center items-center`}>
<MyText style={tw`text-gray-500`}>Failed to load constants</MyText>
</View>
);
}
const initialValues: ConstantFormData = constants.reduce((acc, constant) => {
acc[constant.key] = constant.value ?? ''
return acc
}, {} as ConstantFormData)
return (
<AppContainer>
<View style={tw`flex-1 bg-gray-50 p-4`}>
<MyText style={tw`text-xl font-bold mb-4`}>Customize App Constants</MyText>
<MyText style={tw`text-gray-600 mb-6`}>
Modify application constants. Changes will take effect immediately.
</MyText>
<Formik initialValues={initialValues} onSubmit={handleSubmit}>
{({ handleSubmit, values, setFieldValue }) => (
<View>
{constants.map((constant) => {
if (!CONST_VISIBILITY[constant.key]) {
return null
}
return (
<View key={constant.key} style={tw`mb-4`}>
<ConstantInput
constantKey={constant.key}
value={values[constant.key]}
setFieldValue={setFieldValue}
router={router}
/>
</View>
)
})}
<MyTouchableOpacity
onPress={() => handleSubmit()}
disabled={isUpdating}
style={tw`bg-blue-500 p-4 rounded-lg mt-6 ${isUpdating ? 'opacity-50' : ''}`}
>
<MyText style={tw`text-white text-center font-semibold`}>
{isUpdating ? 'Updating...' : 'Save Changes'}
</MyText>
</MyTouchableOpacity>
</View>
)}
</Formik>
</View>
</AppContainer>
);
}