276 lines
8.9 KiB
TypeScript
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>
|
|
);
|
|
}
|