Compare commits
5 commits
main
...
sqlite_mig
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
84f1ea179f | ||
|
|
b38ff13950 | ||
|
|
e2abc7cb02 | ||
|
|
4f1f52ffee | ||
|
|
71cad727fd |
209 changed files with 11558 additions and 3147 deletions
|
|
@ -6,3 +6,4 @@ apps/users-ui/src
|
||||||
apps/admin-ui/app
|
apps/admin-ui/app
|
||||||
apps/users-ui/src
|
apps/users-ui/src
|
||||||
**/package-lock.json
|
**/package-lock.json
|
||||||
|
test/
|
||||||
|
|
|
||||||
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -7,10 +7,13 @@ yarn-debug.log*
|
||||||
yarn-error.log*
|
yarn-error.log*
|
||||||
lerna-debug.log*
|
lerna-debug.log*
|
||||||
.pnpm-debug.log*
|
.pnpm-debug.log*
|
||||||
|
*.apk
|
||||||
|
|
||||||
# Diagnostic reports (https://nodejs.org/api/report.html)
|
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||||
|
|
||||||
|
test/appBinaries
|
||||||
|
|
||||||
# Runtime data
|
# Runtime data
|
||||||
pids
|
pids
|
||||||
*.pid
|
*.pid
|
||||||
|
|
|
||||||
6
apps/admin-ui/.expo/types/router.d.ts
vendored
6
apps/admin-ui/.expo/types/router.d.ts
vendored
File diff suppressed because one or more lines are too long
|
|
@ -158,6 +158,15 @@ export default function Dashboard() {
|
||||||
iconColor: '#8B5CF6',
|
iconColor: '#8B5CF6',
|
||||||
iconBg: '#F3E8FF',
|
iconBg: '#F3E8FF',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: 'Stocking Schedules',
|
||||||
|
icon: 'schedule',
|
||||||
|
description: 'Manage product stocking schedules',
|
||||||
|
route: '/(drawer)/stocking-schedules',
|
||||||
|
category: 'products',
|
||||||
|
iconColor: '#0EA5E9',
|
||||||
|
iconBg: '#E0F2FE',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: 'Stores',
|
title: 'Stores',
|
||||||
icon: 'store',
|
icon: 'store',
|
||||||
|
|
|
||||||
443
apps/admin-ui/app/(drawer)/stocking-schedules/index.tsx
Normal file
443
apps/admin-ui/app/(drawer)/stocking-schedules/index.tsx
Normal file
|
|
@ -0,0 +1,443 @@
|
||||||
|
import React, { useState } from 'react';
|
||||||
|
import { View, ScrollView, Alert, FlatList, TouchableOpacity } from 'react-native';
|
||||||
|
import {
|
||||||
|
theme,
|
||||||
|
AppContainer,
|
||||||
|
MyText,
|
||||||
|
tw,
|
||||||
|
useManualRefresh,
|
||||||
|
useMarkDataFetchers,
|
||||||
|
MyTouchableOpacity,
|
||||||
|
RawBottomDialog,
|
||||||
|
BottomDialog,
|
||||||
|
} from 'common-ui';
|
||||||
|
import { trpc } from '../../../src/trpc-client';
|
||||||
|
import MaterialIcons from '@expo/vector-icons/MaterialIcons';
|
||||||
|
import { Ionicons, Entypo } from '@expo/vector-icons';
|
||||||
|
import { LinearGradient } from 'expo-linear-gradient';
|
||||||
|
|
||||||
|
import AvailabilityScheduleForm from '../../../components/AvailabilityScheduleForm';
|
||||||
|
|
||||||
|
interface Schedule {
|
||||||
|
id: number;
|
||||||
|
scheduleName: string;
|
||||||
|
time: string;
|
||||||
|
action: 'in' | 'out';
|
||||||
|
createdAt: string;
|
||||||
|
lastUpdated: string;
|
||||||
|
productIds: number[];
|
||||||
|
groupIds: number[];
|
||||||
|
productCount: number;
|
||||||
|
groupCount: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ScheduleItem = ({
|
||||||
|
schedule,
|
||||||
|
onDelete,
|
||||||
|
index,
|
||||||
|
onViewProducts,
|
||||||
|
onViewGroups,
|
||||||
|
onReplicate,
|
||||||
|
}: {
|
||||||
|
schedule: Schedule;
|
||||||
|
onDelete: (id: number) => void;
|
||||||
|
index: number;
|
||||||
|
onViewProducts: (productIds: number[]) => void;
|
||||||
|
onViewGroups: (groupIds: number[]) => void;
|
||||||
|
onReplicate: (schedule: Schedule) => void;
|
||||||
|
}) => {
|
||||||
|
const isIn = schedule.action === 'in';
|
||||||
|
const [menuOpen, setMenuOpen] = useState(false);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style={tw``}>
|
||||||
|
<View style={tw`p-6`}>
|
||||||
|
{/* Top Header: Name & Action Badge */}
|
||||||
|
<View style={tw`flex-row justify-between items-start mb-4`}>
|
||||||
|
<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="schedule"
|
||||||
|
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`}
|
||||||
|
>
|
||||||
|
Schedule Name
|
||||||
|
</MyText>
|
||||||
|
<MyText
|
||||||
|
style={tw`text-xl font-black text-slate-900`}
|
||||||
|
numberOfLines={1}
|
||||||
|
>
|
||||||
|
{schedule.scheduleName}
|
||||||
|
</MyText>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<View style={tw`flex-row items-center`}>
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
tw`px-3 py-1.5 rounded-full flex-row items-center mr-2`,
|
||||||
|
{ backgroundColor: isIn ? '#F0FDF4' : '#FFF1F2' },
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
tw`w-1.5 h-1.5 rounded-full mr-2`,
|
||||||
|
{ backgroundColor: isIn ? '#10B981' : '#E11D48' },
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
<MyText
|
||||||
|
style={[
|
||||||
|
tw`text-[10px] font-black uppercase tracking-tighter`,
|
||||||
|
{ color: isIn ? '#10B981' : '#E11D48' },
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
{isIn ? 'In Stock' : 'Out of Stock'}
|
||||||
|
</MyText>
|
||||||
|
</View>
|
||||||
|
<TouchableOpacity
|
||||||
|
onPress={() => setMenuOpen(true)}
|
||||||
|
style={tw`p-1`}
|
||||||
|
hitSlop={{ top: 10, bottom: 10, left: 10, right: 10 }}
|
||||||
|
>
|
||||||
|
<Entypo name="dots-three-vertical" size={20} color="#9CA3AF" />
|
||||||
|
</TouchableOpacity>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
{/* Menu Dialog */}
|
||||||
|
<BottomDialog open={menuOpen} onClose={() => setMenuOpen(false)}>
|
||||||
|
<View style={tw`p-4`}>
|
||||||
|
<MyText style={tw`text-lg font-bold mb-4`}>{schedule.scheduleName}</MyText>
|
||||||
|
|
||||||
|
<TouchableOpacity
|
||||||
|
onPress={() => {
|
||||||
|
setMenuOpen(false);
|
||||||
|
onReplicate(schedule);
|
||||||
|
}}
|
||||||
|
style={tw`py-4 border-b border-gray-200`}
|
||||||
|
>
|
||||||
|
<View style={tw`flex-row items-center`}>
|
||||||
|
<MaterialIcons name="content-copy" size={20} color="#4B5563" style={tw`mr-3`} />
|
||||||
|
<MyText style={tw`text-base text-gray-800`}>Replicate items</MyText>
|
||||||
|
</View>
|
||||||
|
</TouchableOpacity>
|
||||||
|
|
||||||
|
<TouchableOpacity
|
||||||
|
onPress={() => {
|
||||||
|
setMenuOpen(false);
|
||||||
|
Alert.alert('Coming Soon', 'Edit functionality will be available soon');
|
||||||
|
}}
|
||||||
|
style={tw`py-4 border-b border-gray-200`}
|
||||||
|
>
|
||||||
|
<View style={tw`flex-row items-center`}>
|
||||||
|
<MaterialIcons name="edit" size={20} color="#4B5563" style={tw`mr-3`} />
|
||||||
|
<MyText style={tw`text-base text-gray-800`}>Edit</MyText>
|
||||||
|
</View>
|
||||||
|
</TouchableOpacity>
|
||||||
|
|
||||||
|
<TouchableOpacity
|
||||||
|
onPress={() => {
|
||||||
|
setMenuOpen(false);
|
||||||
|
onDelete(schedule.id);
|
||||||
|
}}
|
||||||
|
style={tw`py-4 border-b border-gray-200`}
|
||||||
|
>
|
||||||
|
<View style={tw`flex-row items-center`}>
|
||||||
|
<MaterialIcons name="delete" size={20} color="#E11D48" style={tw`mr-3`} />
|
||||||
|
<MyText style={tw`text-base text-red-500`}>Delete</MyText>
|
||||||
|
</View>
|
||||||
|
</TouchableOpacity>
|
||||||
|
|
||||||
|
<TouchableOpacity
|
||||||
|
onPress={() => setMenuOpen(false)}
|
||||||
|
style={tw`py-4 mt-2`}
|
||||||
|
>
|
||||||
|
<View style={tw`flex-row items-center`}>
|
||||||
|
<MaterialIcons name="close" size={20} color="#6B7280" style={tw`mr-3`} />
|
||||||
|
<MyText style={tw`text-base text-gray-600`}>Cancel</MyText>
|
||||||
|
</View>
|
||||||
|
</TouchableOpacity>
|
||||||
|
</View>
|
||||||
|
</BottomDialog>
|
||||||
|
|
||||||
|
{/* Middle: Time Banner */}
|
||||||
|
<View
|
||||||
|
style={tw`bg-slate-50 rounded-3xl p-4 flex-row items-center mb-4 border border-slate-100`}
|
||||||
|
>
|
||||||
|
<View
|
||||||
|
style={tw`bg-white w-10 h-10 rounded-2xl items-center justify-center shadow-sm`}
|
||||||
|
>
|
||||||
|
<MaterialIcons name="access-time" size={20} color="#64748B" />
|
||||||
|
</View>
|
||||||
|
<View style={tw`ml-4 flex-1`}>
|
||||||
|
<MyText style={tw`text-slate-900 font-extrabold text-sm`}>
|
||||||
|
{schedule.time}
|
||||||
|
</MyText>
|
||||||
|
<MyText style={tw`text-slate-500 text-[10px] font-bold uppercase`}>
|
||||||
|
Daily at this time
|
||||||
|
</MyText>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
{/* Stats & Actions */}
|
||||||
|
<View style={tw`flex-row items-center justify-between`}>
|
||||||
|
<View style={tw`flex-row items-center`}>
|
||||||
|
<MyTouchableOpacity
|
||||||
|
onPress={() => onViewProducts(schedule.productIds)}
|
||||||
|
style={tw`flex-row items-center mr-4`}
|
||||||
|
>
|
||||||
|
<MaterialIcons name="shopping-bag" size={14} color="#94A3B8" />
|
||||||
|
<MyText style={tw`text-xs font-bold text-brand600 ml-1.5`}>
|
||||||
|
{schedule.productCount} Products
|
||||||
|
</MyText>
|
||||||
|
</MyTouchableOpacity>
|
||||||
|
{schedule.groupCount > 0 && (
|
||||||
|
<MyTouchableOpacity
|
||||||
|
onPress={() => onViewGroups(schedule.groupIds)}
|
||||||
|
style={tw`flex-row items-center`}
|
||||||
|
>
|
||||||
|
<MaterialIcons name="category" size={14} color="#94A3B8" />
|
||||||
|
<MyText style={tw`text-xs font-bold text-brand600 ml-1.5`}>
|
||||||
|
{schedule.groupCount} Groups
|
||||||
|
</MyText>
|
||||||
|
</MyTouchableOpacity>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function StockingSchedules() {
|
||||||
|
const {
|
||||||
|
data: schedules,
|
||||||
|
isLoading,
|
||||||
|
error,
|
||||||
|
refetch,
|
||||||
|
} = trpc.admin.productAvailabilitySchedules.getAll.useQuery();
|
||||||
|
|
||||||
|
const { data: productsData } = trpc.common.product.getAllProductsSummary.useQuery({});
|
||||||
|
const { data: groupsData } = trpc.admin.product.getGroups.useQuery();
|
||||||
|
|
||||||
|
const deleteSchedule = trpc.admin.productAvailabilitySchedules.delete.useMutation();
|
||||||
|
|
||||||
|
const [showCreateForm, setShowCreateForm] = useState(false);
|
||||||
|
|
||||||
|
// Dialog state
|
||||||
|
const [dialogOpen, setDialogOpen] = useState(false);
|
||||||
|
const [dialogType, setDialogType] = useState<'products' | 'groups'>('products');
|
||||||
|
const [selectedIds, setSelectedIds] = useState<number[]>([]);
|
||||||
|
|
||||||
|
// Replication state
|
||||||
|
const [replicatingSchedule, setReplicatingSchedule] = useState<Schedule | null>(null);
|
||||||
|
|
||||||
|
useManualRefresh(refetch);
|
||||||
|
|
||||||
|
useMarkDataFetchers(() => {
|
||||||
|
refetch();
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleCreate = () => {
|
||||||
|
setShowCreateForm(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDelete = (id: number) => {
|
||||||
|
Alert.alert(
|
||||||
|
'Delete Schedule',
|
||||||
|
'Are you sure you want to delete this schedule? This action cannot be undone.',
|
||||||
|
[
|
||||||
|
{ text: 'Cancel', style: 'cancel' },
|
||||||
|
{
|
||||||
|
text: 'Delete',
|
||||||
|
style: 'destructive',
|
||||||
|
onPress: () => {
|
||||||
|
deleteSchedule.mutate(
|
||||||
|
{ id },
|
||||||
|
{
|
||||||
|
onSuccess: () => {
|
||||||
|
refetch();
|
||||||
|
},
|
||||||
|
onError: (error: any) => {
|
||||||
|
Alert.alert('Error', error.message || 'Failed to delete schedule');
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleViewProducts = (productIds: number[]) => {
|
||||||
|
setDialogType('products');
|
||||||
|
setSelectedIds(productIds);
|
||||||
|
setDialogOpen(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleViewGroups = (groupIds: number[]) => {
|
||||||
|
setDialogType('groups');
|
||||||
|
setSelectedIds(groupIds);
|
||||||
|
setDialogOpen(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleReplicate = (schedule: Schedule) => {
|
||||||
|
setReplicatingSchedule(schedule);
|
||||||
|
setShowCreateForm(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCloseForm = () => {
|
||||||
|
setShowCreateForm(false);
|
||||||
|
setReplicatingSchedule(null);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Get product/group names from IDs
|
||||||
|
const getProductNames = () => {
|
||||||
|
const allProducts = productsData?.products || [];
|
||||||
|
return selectedIds.map(id => {
|
||||||
|
const product = allProducts.find(p => p.id === id);
|
||||||
|
return product?.name || `Product #${id}`;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const getGroupNames = () => {
|
||||||
|
const allGroups = groupsData?.groups || [];
|
||||||
|
return selectedIds.map(id => {
|
||||||
|
const group = allGroups.find(g => g.id === id);
|
||||||
|
return group?.groupName || `Group #${id}`;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
if (showCreateForm) {
|
||||||
|
return (
|
||||||
|
<AvailabilityScheduleForm
|
||||||
|
onClose={handleCloseForm}
|
||||||
|
onSuccess={() => {
|
||||||
|
refetch();
|
||||||
|
handleCloseForm();
|
||||||
|
}}
|
||||||
|
initialProductIds={replicatingSchedule?.productIds}
|
||||||
|
initialGroupIds={replicatingSchedule?.groupIds}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isLoading) {
|
||||||
|
return (
|
||||||
|
<AppContainer>
|
||||||
|
<View style={tw`flex-1 justify-center items-center`}>
|
||||||
|
<MyText style={tw`text-gray-600`}>Loading schedules...</MyText>
|
||||||
|
</View>
|
||||||
|
</AppContainer>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
return (
|
||||||
|
<AppContainer>
|
||||||
|
<View style={tw`flex-1 justify-center items-center`}>
|
||||||
|
<MyText style={tw`text-red-600`}>Error loading schedules</MyText>
|
||||||
|
</View>
|
||||||
|
</AppContainer>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<AppContainer>
|
||||||
|
<View style={tw`flex-1 bg-white h-full`}>
|
||||||
|
<ScrollView
|
||||||
|
style={tw`flex-1`}
|
||||||
|
contentContainerStyle={tw`pt-2 pb-32`}
|
||||||
|
showsVerticalScrollIndicator={false}
|
||||||
|
>
|
||||||
|
{schedules && schedules.length === 0 ? (
|
||||||
|
<View style={tw`flex-1 justify-center items-center py-20`}>
|
||||||
|
<View
|
||||||
|
style={tw`w-24 h-24 bg-slate-50 rounded-full items-center justify-center mb-6`}
|
||||||
|
>
|
||||||
|
<Ionicons name="time-outline" size={48} color="#94A3B8" />
|
||||||
|
</View>
|
||||||
|
<MyText
|
||||||
|
style={tw`text-slate-900 text-xl font-black tracking-tight`}
|
||||||
|
>
|
||||||
|
No Schedules Yet
|
||||||
|
</MyText>
|
||||||
|
<MyText
|
||||||
|
style={tw`text-slate-500 text-center mt-2 font-medium px-8`}
|
||||||
|
>
|
||||||
|
Start by creating your first availability schedule using the
|
||||||
|
button below.
|
||||||
|
</MyText>
|
||||||
|
</View>
|
||||||
|
) : (
|
||||||
|
schedules?.map((schedule, index) => (
|
||||||
|
<React.Fragment key={schedule.id}>
|
||||||
|
<ScheduleItem
|
||||||
|
schedule={schedule}
|
||||||
|
index={index}
|
||||||
|
onDelete={handleDelete}
|
||||||
|
onViewProducts={handleViewProducts}
|
||||||
|
onViewGroups={handleViewGroups}
|
||||||
|
onReplicate={handleReplicate}
|
||||||
|
/>
|
||||||
|
{index < schedules.length - 1 && (
|
||||||
|
<View style={tw`h-px bg-slate-200 w-full`} />
|
||||||
|
)}
|
||||||
|
</React.Fragment>
|
||||||
|
))
|
||||||
|
)}
|
||||||
|
</ScrollView>
|
||||||
|
</View>
|
||||||
|
</AppContainer>
|
||||||
|
<MyTouchableOpacity
|
||||||
|
onPress={handleCreate}
|
||||||
|
activeOpacity={0.95}
|
||||||
|
style={tw`absolute bottom-8 right-6 shadow-2xl z-50`}
|
||||||
|
>
|
||||||
|
<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>
|
||||||
|
|
||||||
|
{/* Products/Groups Dialog */}
|
||||||
|
<RawBottomDialog open={dialogOpen} onClose={() => setDialogOpen(false)}>
|
||||||
|
<View style={tw`p-4`}>
|
||||||
|
<MyText style={tw`text-lg font-bold mb-4`}>
|
||||||
|
{dialogType === 'products' ? 'Products' : 'Groups'}
|
||||||
|
</MyText>
|
||||||
|
<FlatList
|
||||||
|
data={dialogType === 'products' ? getProductNames() : getGroupNames()}
|
||||||
|
keyExtractor={(item, index) => index.toString()}
|
||||||
|
renderItem={({ item }) => (
|
||||||
|
<View style={tw`py-3 border-b border-gray-100`}>
|
||||||
|
<MyText style={tw`text-base text-gray-800`}>{item}</MyText>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
showsVerticalScrollIndicator={false}
|
||||||
|
style={tw`max-h-80`}
|
||||||
|
ListEmptyComponent={
|
||||||
|
<View style={tw`py-8 items-center`}>
|
||||||
|
<MyText style={tw`text-gray-500`}>
|
||||||
|
No {dialogType} found
|
||||||
|
</MyText>
|
||||||
|
</View>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
</RawBottomDialog>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
237
apps/admin-ui/components/AvailabilityScheduleForm.tsx
Normal file
237
apps/admin-ui/components/AvailabilityScheduleForm.tsx
Normal file
|
|
@ -0,0 +1,237 @@
|
||||||
|
import React, { useState } from 'react';
|
||||||
|
import { View, TouchableOpacity, Alert, ScrollView } from 'react-native';
|
||||||
|
import { useFormik } from 'formik';
|
||||||
|
import { MyText, tw, MyTextInput, MyTouchableOpacity, DateTimePickerMod } from 'common-ui';
|
||||||
|
import MaterialIcons from '@expo/vector-icons/MaterialIcons';
|
||||||
|
import ProductsSelector from './ProductsSelector';
|
||||||
|
import { trpc } from '../src/trpc-client';
|
||||||
|
|
||||||
|
interface AvailabilityScheduleFormProps {
|
||||||
|
onClose: () => void;
|
||||||
|
onSuccess: () => void;
|
||||||
|
initialProductIds?: number[];
|
||||||
|
initialGroupIds?: number[];
|
||||||
|
}
|
||||||
|
|
||||||
|
const AvailabilityScheduleForm: React.FC<AvailabilityScheduleFormProps> = ({
|
||||||
|
onClose,
|
||||||
|
onSuccess,
|
||||||
|
initialProductIds,
|
||||||
|
initialGroupIds,
|
||||||
|
}) => {
|
||||||
|
const createSchedule = trpc.admin.productAvailabilitySchedules.create.useMutation();
|
||||||
|
const { data: groupsData } = trpc.admin.product.getGroups.useQuery();
|
||||||
|
|
||||||
|
// Map groups data to match ProductsSelector types (convert price from string to number)
|
||||||
|
const groups = (groupsData?.groups || []).map(group => ({
|
||||||
|
...group,
|
||||||
|
products: group.products.map(product => ({
|
||||||
|
...product,
|
||||||
|
price: parseFloat(product.price as unknown as string) || 0,
|
||||||
|
})),
|
||||||
|
}));
|
||||||
|
|
||||||
|
const formik = useFormik({
|
||||||
|
initialValues: {
|
||||||
|
scheduleName: '',
|
||||||
|
timeDate: null as Date | null,
|
||||||
|
action: 'in' as 'in' | 'out',
|
||||||
|
productIds: initialProductIds || ([] as number[]),
|
||||||
|
groupIds: initialGroupIds || ([] as number[]),
|
||||||
|
},
|
||||||
|
validate: (values) => {
|
||||||
|
const errors: {[key: string]: string} = {};
|
||||||
|
|
||||||
|
if (!values.scheduleName.trim()) {
|
||||||
|
errors.scheduleName = 'Schedule name is required';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!values.timeDate) {
|
||||||
|
errors.timeDate = 'Time is required';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!values.action) {
|
||||||
|
errors.action = 'Action is required';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (values.productIds.length === 0) {
|
||||||
|
errors.productIds = 'At least one product must be selected';
|
||||||
|
}
|
||||||
|
|
||||||
|
return errors;
|
||||||
|
},
|
||||||
|
onSubmit: async (values) => {
|
||||||
|
try {
|
||||||
|
// Convert Date to HH:MM string
|
||||||
|
const hours = values.timeDate!.getHours().toString().padStart(2, '0');
|
||||||
|
const minutes = values.timeDate!.getMinutes().toString().padStart(2, '0');
|
||||||
|
const timeString = `${hours}:${minutes}`;
|
||||||
|
|
||||||
|
await createSchedule.mutateAsync({
|
||||||
|
scheduleName: values.scheduleName,
|
||||||
|
time: timeString,
|
||||||
|
action: values.action,
|
||||||
|
productIds: values.productIds,
|
||||||
|
groupIds: values.groupIds,
|
||||||
|
});
|
||||||
|
|
||||||
|
Alert.alert('Success', 'Schedule created successfully');
|
||||||
|
onSuccess();
|
||||||
|
onClose();
|
||||||
|
} catch (error: any) {
|
||||||
|
Alert.alert('Error', error.message || 'Failed to create schedule');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const actionOptions = [
|
||||||
|
{ label: 'In Stock', value: 'in' },
|
||||||
|
{ label: 'Out of Stock', value: 'out' },
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style={tw`flex-1 bg-white`}>
|
||||||
|
{/* Header */}
|
||||||
|
<View style={tw`flex-row items-center justify-between p-4 border-b border-gray-200 bg-white`}>
|
||||||
|
<MyText style={tw`text-xl font-bold text-gray-900`}>
|
||||||
|
Create Availability Schedule
|
||||||
|
</MyText>
|
||||||
|
<MyTouchableOpacity onPress={onClose}>
|
||||||
|
<MaterialIcons name="close" size={24} color="#6B7280" />
|
||||||
|
</MyTouchableOpacity>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<ScrollView style={tw`flex-1 p-4`} showsVerticalScrollIndicator={false}>
|
||||||
|
{/* Schedule Name */}
|
||||||
|
<View style={tw`mb-4`}>
|
||||||
|
<MyText style={tw`text-sm font-medium text-gray-700 mb-2`}>
|
||||||
|
Schedule Name
|
||||||
|
</MyText>
|
||||||
|
<MyTextInput
|
||||||
|
placeholder="Enter schedule name"
|
||||||
|
value={formik.values.scheduleName}
|
||||||
|
onChangeText={formik.handleChange('scheduleName')}
|
||||||
|
onBlur={formik.handleBlur('scheduleName')}
|
||||||
|
style={tw`border rounded-lg p-3 ${
|
||||||
|
formik.touched.scheduleName && formik.errors.scheduleName
|
||||||
|
? 'border-red-500'
|
||||||
|
: 'border-gray-300'
|
||||||
|
}`}
|
||||||
|
/>
|
||||||
|
{formik.touched.scheduleName && formik.errors.scheduleName && (
|
||||||
|
<MyText style={tw`text-red-500 text-xs mt-1`}>
|
||||||
|
{formik.errors.scheduleName}
|
||||||
|
</MyText>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
|
||||||
|
{/* Time */}
|
||||||
|
<View style={tw`mb-4`}>
|
||||||
|
<MyText style={tw`text-sm font-medium text-gray-700 mb-2`}>
|
||||||
|
Time
|
||||||
|
</MyText>
|
||||||
|
<DateTimePickerMod
|
||||||
|
value={formik.values.timeDate}
|
||||||
|
setValue={(date) => formik.setFieldValue('timeDate', date)}
|
||||||
|
timeOnly={true}
|
||||||
|
showLabels={false}
|
||||||
|
/>
|
||||||
|
{formik.touched.timeDate && formik.errors.timeDate && (
|
||||||
|
<MyText style={tw`text-red-500 text-xs mt-1`}>
|
||||||
|
{formik.errors.timeDate}
|
||||||
|
</MyText>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
|
||||||
|
{/* Action */}
|
||||||
|
<View style={tw`mb-4`}>
|
||||||
|
<MyText style={tw`text-sm font-medium text-gray-700 mb-2`}>
|
||||||
|
Action
|
||||||
|
</MyText>
|
||||||
|
<View style={tw`flex-row gap-3`}>
|
||||||
|
{actionOptions.map((option) => (
|
||||||
|
<TouchableOpacity
|
||||||
|
key={option.value}
|
||||||
|
onPress={() => formik.setFieldValue('action', option.value)}
|
||||||
|
style={tw`flex-1 flex-row items-center p-4 rounded-lg border ${
|
||||||
|
formik.values.action === option.value
|
||||||
|
? 'bg-blue-50 border-blue-500'
|
||||||
|
: 'bg-white border-gray-300'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<View
|
||||||
|
style={tw`w-5 h-5 rounded-full border-2 mr-3 items-center justify-center ${
|
||||||
|
formik.values.action === option.value
|
||||||
|
? 'border-blue-500'
|
||||||
|
: 'border-gray-300'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{formik.values.action === option.value && (
|
||||||
|
<View style={tw`w-3 h-3 rounded-full bg-blue-500`} />
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
<MyText
|
||||||
|
style={tw`font-medium ${
|
||||||
|
formik.values.action === option.value
|
||||||
|
? 'text-blue-700'
|
||||||
|
: 'text-gray-700'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{option.label}
|
||||||
|
</MyText>
|
||||||
|
</TouchableOpacity>
|
||||||
|
))}
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
{/* Products and Groups */}
|
||||||
|
<View style={tw`mb-4`}>
|
||||||
|
<MyText style={tw`text-sm font-medium text-gray-700 mb-2`}>
|
||||||
|
Products & Groups
|
||||||
|
</MyText>
|
||||||
|
<ProductsSelector
|
||||||
|
value={formik.values.productIds}
|
||||||
|
onChange={(value) => formik.setFieldValue('productIds', value)}
|
||||||
|
groups={groups}
|
||||||
|
selectedGroupIds={formik.values.groupIds}
|
||||||
|
onGroupChange={(groupIds) => formik.setFieldValue('groupIds', groupIds)}
|
||||||
|
showGroups={true}
|
||||||
|
label="Select Products"
|
||||||
|
placeholder="Select products for this schedule"
|
||||||
|
/>
|
||||||
|
{formik.touched.productIds && formik.errors.productIds && (
|
||||||
|
<MyText style={tw`text-red-500 text-xs mt-1`}>
|
||||||
|
{formik.errors.productIds}
|
||||||
|
</MyText>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
|
||||||
|
{/* Spacer for bottom padding */}
|
||||||
|
<View style={tw`h-24`} />
|
||||||
|
</ScrollView>
|
||||||
|
|
||||||
|
{/* Footer Buttons */}
|
||||||
|
<View style={tw`p-4 border-t border-gray-200 bg-white flex-row gap-3`}>
|
||||||
|
<MyTouchableOpacity
|
||||||
|
onPress={onClose}
|
||||||
|
style={tw`flex-1 py-3 px-4 rounded-lg border border-gray-300 items-center`}
|
||||||
|
>
|
||||||
|
<MyText style={tw`font-medium text-gray-700`}>Cancel</MyText>
|
||||||
|
</MyTouchableOpacity>
|
||||||
|
<MyTouchableOpacity
|
||||||
|
onPress={() => formik.handleSubmit()}
|
||||||
|
disabled={formik.isSubmitting}
|
||||||
|
style={tw`flex-1 py-3 px-4 rounded-lg bg-blue-600 items-center ${
|
||||||
|
formik.isSubmitting ? 'opacity-50' : ''
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<MyText style={tw`font-medium text-white`}>
|
||||||
|
{formik.isSubmitting ? 'Creating...' : 'Create Schedule'}
|
||||||
|
</MyText>
|
||||||
|
</MyTouchableOpacity>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default AvailabilityScheduleForm;
|
||||||
|
|
@ -5,8 +5,8 @@
|
||||||
},
|
},
|
||||||
"build": {
|
"build": {
|
||||||
"development": {
|
"development": {
|
||||||
"developmentClient": true,
|
"distribution": "internal",
|
||||||
"distribution": "internal"
|
"autoIncrement": true
|
||||||
},
|
},
|
||||||
"preview": {
|
"preview": {
|
||||||
"distribution": "internal",
|
"distribution": "internal",
|
||||||
|
|
|
||||||
14
apps/backend/drizzle/0077_wakeful_norrin_radd.sql
Normal file
14
apps/backend/drizzle/0077_wakeful_norrin_radd.sql
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
CREATE TYPE "public"."product_availability_action" AS ENUM('in', 'out');--> statement-breakpoint
|
||||||
|
CREATE TABLE "mf"."product_availability_schedules" (
|
||||||
|
"id" integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY (sequence name "mf"."product_availability_schedules_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 2147483647 START WITH 1 CACHE 1),
|
||||||
|
"time" varchar(10) NOT NULL,
|
||||||
|
"schedule_name" varchar(255) NOT NULL,
|
||||||
|
"action" "product_availability_action" NOT NULL,
|
||||||
|
"product_ids" integer[] DEFAULT '{}' NOT NULL,
|
||||||
|
"group_ids" integer[] DEFAULT '{}' NOT NULL,
|
||||||
|
"created_at" timestamp DEFAULT now() NOT NULL,
|
||||||
|
"last_updated" timestamp DEFAULT now() NOT NULL,
|
||||||
|
CONSTRAINT "product_availability_schedules_schedule_name_unique" UNIQUE("schedule_name")
|
||||||
|
);
|
||||||
|
--> statement-breakpoint
|
||||||
|
ALTER TABLE "mf"."product_info" ADD COLUMN "scheduled_availability" boolean DEFAULT true NOT NULL;
|
||||||
3965
apps/backend/drizzle/meta/0077_snapshot.json
Normal file
3965
apps/backend/drizzle/meta/0077_snapshot.json
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -540,6 +540,13 @@
|
||||||
"when": 1772637259874,
|
"when": 1772637259874,
|
||||||
"tag": "0076_sturdy_wolverine",
|
"tag": "0076_sturdy_wolverine",
|
||||||
"breakpoints": true
|
"breakpoints": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idx": 77,
|
||||||
|
"version": "7",
|
||||||
|
"when": 1773927855512,
|
||||||
|
"tag": "0077_wakeful_norrin_radd",
|
||||||
|
"breakpoints": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
@ -93,6 +93,8 @@ export const units = mf.table('units', {
|
||||||
unq_short_notation: unique('unique_short_notation').on(t.shortNotation),
|
unq_short_notation: unique('unique_short_notation').on(t.shortNotation),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
export const productAvailabilityActionEnum = pgEnum('product_availability_action', ['in', 'out']);
|
||||||
|
|
||||||
export const productInfo = mf.table('product_info', {
|
export const productInfo = mf.table('product_info', {
|
||||||
id: integer().primaryKey().generatedAlwaysAsIdentity(),
|
id: integer().primaryKey().generatedAlwaysAsIdentity(),
|
||||||
name: varchar({ length: 255 }).notNull(),
|
name: varchar({ length: 255 }).notNull(),
|
||||||
|
|
@ -106,10 +108,22 @@ export const productInfo = mf.table('product_info', {
|
||||||
isSuspended: boolean('is_suspended').notNull().default(false),
|
isSuspended: boolean('is_suspended').notNull().default(false),
|
||||||
isFlashAvailable: boolean('is_flash_available').notNull().default(false),
|
isFlashAvailable: boolean('is_flash_available').notNull().default(false),
|
||||||
flashPrice: numeric('flash_price', { precision: 10, scale: 2 }),
|
flashPrice: numeric('flash_price', { precision: 10, scale: 2 }),
|
||||||
createdAt: timestamp('created_at').notNull().defaultNow(),
|
createdAt: timestamp('created_at').notNull().defaultNow(),
|
||||||
incrementStep: real('increment_step').notNull().default(1),
|
incrementStep: real('increment_step').notNull().default(1),
|
||||||
productQuantity: real('product_quantity').notNull().default(1),
|
productQuantity: real('product_quantity').notNull().default(1),
|
||||||
storeId: integer('store_id').references(() => storeInfo.id),
|
storeId: integer('store_id').references(() => storeInfo.id),
|
||||||
|
scheduledAvailability: boolean('scheduled_availability').notNull().default(true),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const productAvailabilitySchedules = mf.table('product_availability_schedules', {
|
||||||
|
id: integer().primaryKey().generatedAlwaysAsIdentity(),
|
||||||
|
time: varchar('time', { length: 10 }).notNull(),
|
||||||
|
scheduleName: varchar('schedule_name', { length: 255 }).notNull().unique(),
|
||||||
|
action: productAvailabilityActionEnum('action').notNull(),
|
||||||
|
productIds: integer('product_ids').array().notNull().default([]),
|
||||||
|
groupIds: integer('group_ids').array().notNull().default([]),
|
||||||
|
createdAt: timestamp('created_at').notNull().defaultNow(),
|
||||||
|
lastUpdated: timestamp('last_updated').notNull().defaultNow(),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const productGroupInfo = mf.table('product_group_info', {
|
export const productGroupInfo = mf.table('product_group_info', {
|
||||||
|
|
@ -687,3 +701,6 @@ export const userIncidentsRelations = relations(userIncidents, ({ one }) => ({
|
||||||
order: one(orders, { fields: [userIncidents.orderId], references: [orders.id] }),
|
order: one(orders, { fields: [userIncidents.orderId], references: [orders.id] }),
|
||||||
addedBy: one(staffUsers, { fields: [userIncidents.addedBy], references: [staffUsers.id] }),
|
addedBy: one(staffUsers, { fields: [userIncidents.addedBy], references: [staffUsers.id] }),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
export const productAvailabilitySchedulesRelations = relations(productAvailabilitySchedules, ({}) => ({
|
||||||
|
}));
|
||||||
|
|
|
||||||
|
|
@ -1,85 +1,109 @@
|
||||||
import * as cron from 'node-cron';
|
import * as cron from 'node-cron';
|
||||||
import { db } from '@/src/db/db_index'
|
import { db } from '@/src/db/db_index'
|
||||||
import { productInfo, keyValStore } from '@/src/db/schema'
|
import { productInfo, productAvailabilitySchedules } from '@/src/db/schema'
|
||||||
import { inArray, eq } from 'drizzle-orm';
|
import { inArray } from 'drizzle-orm';
|
||||||
import { CONST_KEYS } from '@/src/lib/const-keys'
|
import { initializeAllStores } from '../stores/store-initializer';
|
||||||
import { computeConstants } from '@/src/lib/const-store'
|
|
||||||
|
|
||||||
|
// Module-level storage for cron jobs
|
||||||
|
const scheduleJobs: Map<string, cron.ScheduledTask> = new Map();
|
||||||
|
|
||||||
const MUTTON_ITEMS = [
|
// Stop all existing schedule-based jobs
|
||||||
12, //Lamb mutton
|
const stopAllScheduleJobs = () => {
|
||||||
14, // Mutton Boti
|
for (const [time, job] of scheduleJobs) {
|
||||||
35, //Mutton Kheema
|
job.stop();
|
||||||
84, //Mutton Brain
|
console.log(`Stopped cron job for ${time}`);
|
||||||
4, //Mutton
|
}
|
||||||
86, //Mutton Chops
|
scheduleJobs.clear();
|
||||||
87, //Mutton Soup bones
|
|
||||||
85 //Mutton paya
|
|
||||||
];
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export const startAutomatedJobs = () => {
|
|
||||||
// Job to disable flash delivery for mutton at 12 PM daily
|
|
||||||
cron.schedule('0 12 * * *', async () => {
|
|
||||||
try {
|
|
||||||
console.log('Disabling flash delivery for products at 12 PM');
|
|
||||||
await db
|
|
||||||
.update(productInfo)
|
|
||||||
.set({ isFlashAvailable: false })
|
|
||||||
.where(inArray(productInfo.id, MUTTON_ITEMS));
|
|
||||||
console.log('Flash delivery disabled successfully');
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error disabling flash delivery:', error);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Job to enable flash delivery for mutton at 6 AM daily
|
|
||||||
cron.schedule('0 6 * * *', async () => {
|
|
||||||
try {
|
|
||||||
console.log('Enabling flash delivery for products at 5 AM');
|
|
||||||
await db
|
|
||||||
.update(productInfo)
|
|
||||||
.set({ isFlashAvailable: true })
|
|
||||||
.where(inArray(productInfo.id, MUTTON_ITEMS));
|
|
||||||
console.log('Flash delivery enabled successfully');
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error enabling flash delivery:', error);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Job to disable flash delivery feature at 9 PM daily
|
|
||||||
cron.schedule('0 21 * * *', async () => {
|
|
||||||
try {
|
|
||||||
console.log('Disabling flash delivery feature at 9 PM');
|
|
||||||
await db
|
|
||||||
.update(keyValStore)
|
|
||||||
.set({ value: false })
|
|
||||||
.where(eq(keyValStore.key, CONST_KEYS.isFlashDeliveryEnabled));
|
|
||||||
await computeConstants(); // Refresh Redis cache
|
|
||||||
console.log('Flash delivery feature disabled successfully');
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error disabling flash delivery feature:', error);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Job to enable flash delivery feature at 6 AM daily
|
|
||||||
cron.schedule('0 6 * * *', async () => {
|
|
||||||
try {
|
|
||||||
console.log('Enabling flash delivery feature at 6 AM');
|
|
||||||
await db
|
|
||||||
.update(keyValStore)
|
|
||||||
.set({ value: true })
|
|
||||||
.where(eq(keyValStore.key, CONST_KEYS.isFlashDeliveryEnabled));
|
|
||||||
await computeConstants(); // Refresh Redis cache
|
|
||||||
console.log('Flash delivery feature enabled successfully');
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error enabling flash delivery feature:', error);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
console.log('Automated jobs scheduled');
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Optional: Call on import if desired, or export and call in main app
|
// Main function to refresh jobs (called on init and after schedule changes)
|
||||||
// startAutomatedJobs();
|
export const refreshScheduleJobs = async (): Promise<void> => {
|
||||||
|
// Stop existing jobs
|
||||||
|
stopAllScheduleJobs();
|
||||||
|
|
||||||
|
// Fetch all schedules from DB
|
||||||
|
const schedules = await db.query.productAvailabilitySchedules.findMany();
|
||||||
|
|
||||||
|
if (schedules.length === 0) {
|
||||||
|
console.log('No schedules found, no jobs created');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Group schedules by time
|
||||||
|
const schedulesByTime = new Map<string, typeof schedules>();
|
||||||
|
|
||||||
|
for (const schedule of schedules) {
|
||||||
|
if (!schedulesByTime.has(schedule.time)) {
|
||||||
|
schedulesByTime.set(schedule.time, []);
|
||||||
|
}
|
||||||
|
schedulesByTime.get(schedule.time)!.push(schedule);
|
||||||
|
}
|
||||||
|
|
||||||
|
// For each time slot, resolve conflicts and create job
|
||||||
|
for (const [time, timeSchedules] of schedulesByTime) {
|
||||||
|
// Sort by ID descending (highest ID = latest = wins in conflicts)
|
||||||
|
const sortedSchedules = timeSchedules.sort((a, b) => b.id - a.id);
|
||||||
|
|
||||||
|
// Build final product states (later schedules override earlier ones)
|
||||||
|
const productStates = new Map<number, 'in' | 'out'>();
|
||||||
|
|
||||||
|
for (const schedule of sortedSchedules) {
|
||||||
|
for (const productId of schedule.productIds) {
|
||||||
|
if (!productStates.has(productId)) {
|
||||||
|
productStates.set(productId, schedule.action);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Separate into in-stock and out-of-stock lists
|
||||||
|
const toTurnOn: number[] = [];
|
||||||
|
const toTurnOff: number[] = [];
|
||||||
|
|
||||||
|
for (const [productId, action] of productStates) {
|
||||||
|
if (action === 'in') {
|
||||||
|
toTurnOn.push(productId);
|
||||||
|
} else {
|
||||||
|
toTurnOff.push(productId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create cron schedule from time (HH:mm to cron format)
|
||||||
|
const [hours, minutes] = time.split(':');
|
||||||
|
const cronExpression = `${minutes} ${hours} * * *`;
|
||||||
|
|
||||||
|
// Create and store the job
|
||||||
|
const job = cron.schedule(cronExpression, async () => {
|
||||||
|
console.log(`Running scheduled availability job for ${time}`);
|
||||||
|
|
||||||
|
// Batch update in single queries
|
||||||
|
if (toTurnOn.length > 0) {
|
||||||
|
await db.update(productInfo)
|
||||||
|
.set({ isOutOfStock: false })
|
||||||
|
.where(inArray(productInfo.id, toTurnOn));
|
||||||
|
console.log(`[${time}] Turned ON ${toTurnOn.length} products`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (toTurnOff.length > 0) {
|
||||||
|
await db.update(productInfo)
|
||||||
|
.set({ isOutOfStock: true })
|
||||||
|
.where(inArray(productInfo.id, toTurnOff));
|
||||||
|
console.log(`[${time}] Turned OFF ${toTurnOff.length} products`);
|
||||||
|
}
|
||||||
|
|
||||||
|
initializeAllStores();
|
||||||
|
});
|
||||||
|
|
||||||
|
scheduleJobs.set(time, job);
|
||||||
|
console.log(`Created cron job for ${time} (${toTurnOn.length} ON, ${toTurnOff.length} OFF)`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Initialize all automated jobs
|
||||||
|
export const startAutomatedJobs = () => {
|
||||||
|
// Only schedule-based jobs (flash delivery jobs removed)
|
||||||
|
refreshScheduleJobs().catch(err => {
|
||||||
|
console.error('Failed to initialize schedule jobs:', err);
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('Automated jobs scheduled');
|
||||||
|
};
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import { initializeUserNegativityStore } from '@/src/stores/user-negativity-stor
|
||||||
import { startOrderHandler, startCancellationHandler, publishOrder } from '@/src/lib/post-order-handler'
|
import { startOrderHandler, startCancellationHandler, publishOrder } from '@/src/lib/post-order-handler'
|
||||||
import { deleteOrders } from '@/src/lib/delete-orders'
|
import { deleteOrders } from '@/src/lib/delete-orders'
|
||||||
import { createAllCacheFiles } from '@/src/lib/cloud_cache'
|
import { createAllCacheFiles } from '@/src/lib/cloud_cache'
|
||||||
|
import { verifyProductsAvailabilityBySchedule } from './manage-scheduled-availability'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize all application services
|
* Initialize all application services
|
||||||
|
|
@ -18,7 +19,8 @@ import { createAllCacheFiles } from '@/src/lib/cloud_cache'
|
||||||
export const initFunc = async (): Promise<void> => {
|
export const initFunc = async (): Promise<void> => {
|
||||||
try {
|
try {
|
||||||
console.log('Starting application initialization...');
|
console.log('Starting application initialization...');
|
||||||
|
|
||||||
|
await verifyProductsAvailabilityBySchedule(false);
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
initializeAllStores(),
|
initializeAllStores(),
|
||||||
initializeUserNegativityStore(),
|
initializeUserNegativityStore(),
|
||||||
|
|
|
||||||
129
apps/backend/src/lib/manage-scheduled-availability.ts
Normal file
129
apps/backend/src/lib/manage-scheduled-availability.ts
Normal file
|
|
@ -0,0 +1,129 @@
|
||||||
|
import { db } from '@/src/db/db_index'
|
||||||
|
import { productInfo, productAvailabilitySchedules } from '@/src/db/schema'
|
||||||
|
import { eq, inArray } from 'drizzle-orm';
|
||||||
|
import { initializeAllStores } from '@/src/stores/store-initializer';
|
||||||
|
import dayjs from 'dayjs';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all products that should be in stock or out of stock based on current schedules
|
||||||
|
* Only processes products that are actually involved in availability schedules
|
||||||
|
* Automatically updates products that need to change their availability status
|
||||||
|
* @returns Promise<{ inStock: number[], outOfStock: number[], changed: number[] }>
|
||||||
|
*/
|
||||||
|
export async function verifyProductsAvailabilityBySchedule(reInitialize:boolean = false): Promise<{
|
||||||
|
inStock: number[];
|
||||||
|
outOfStock: number[];
|
||||||
|
changed: number[];
|
||||||
|
}> {
|
||||||
|
// Get all schedules
|
||||||
|
const allSchedules = await db.query.productAvailabilitySchedules.findMany();
|
||||||
|
|
||||||
|
// Extract all unique product IDs from all schedules
|
||||||
|
const allScheduledProductIds = new Set<number>();
|
||||||
|
for (const schedule of allSchedules) {
|
||||||
|
for (const productId of schedule.productIds) {
|
||||||
|
allScheduledProductIds.add(productId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If no products are in any schedule, return empty arrays
|
||||||
|
if (allScheduledProductIds.size === 0) {
|
||||||
|
return { inStock: [], outOfStock: [], changed: [] };
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get current time
|
||||||
|
const currentTime = dayjs().format('HH:mm');
|
||||||
|
|
||||||
|
const computedInStock: number[] = [];
|
||||||
|
const computedOutOfStock: number[] = [];
|
||||||
|
|
||||||
|
// Process each product that is involved in schedules
|
||||||
|
for (const productId of allScheduledProductIds) {
|
||||||
|
// Find applicable schedules for this product
|
||||||
|
const applicableSchedules = allSchedules.filter(schedule => {
|
||||||
|
return schedule.productIds.includes(productId);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Filter active schedules (time <= current time)
|
||||||
|
const activeSchedules = applicableSchedules.filter(schedule =>
|
||||||
|
schedule.time <= currentTime
|
||||||
|
);
|
||||||
|
|
||||||
|
if (activeSchedules.length === 0) {
|
||||||
|
// No active schedule applies - skip this product
|
||||||
|
// (we only care about products with active schedule rules)
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get most recent schedule
|
||||||
|
const mostRecentSchedule = activeSchedules.sort((a, b) => {
|
||||||
|
if (a.time !== b.time) {
|
||||||
|
return b.time.localeCompare(a.time);
|
||||||
|
}
|
||||||
|
return b.id - a.id;
|
||||||
|
})[0];
|
||||||
|
|
||||||
|
// Categorize based on schedule action
|
||||||
|
if (mostRecentSchedule.action === 'in') {
|
||||||
|
computedInStock.push(productId);
|
||||||
|
} else {
|
||||||
|
computedOutOfStock.push(productId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Query products to check current availability status
|
||||||
|
const allProductIds = [...computedInStock, ...computedOutOfStock];
|
||||||
|
|
||||||
|
if (allProductIds.length === 0) {
|
||||||
|
return { inStock: [], outOfStock: [], changed: [] };
|
||||||
|
}
|
||||||
|
|
||||||
|
const products = await db.query.productInfo.findMany({
|
||||||
|
where: inArray(productInfo.id, allProductIds),
|
||||||
|
});
|
||||||
|
|
||||||
|
// Find products that need to change
|
||||||
|
const toMarkInStock: number[] = [];
|
||||||
|
const toMarkOutOfStock: number[] = [];
|
||||||
|
const changed: number[] = [];
|
||||||
|
|
||||||
|
for (const product of products) {
|
||||||
|
const shouldBeInStock = computedInStock.includes(product.id);
|
||||||
|
const currentlyOutOfStock = product.isOutOfStock;
|
||||||
|
|
||||||
|
if (shouldBeInStock && currentlyOutOfStock) {
|
||||||
|
// Should be in stock but currently out of stock - needs change
|
||||||
|
toMarkInStock.push(product.id);
|
||||||
|
changed.push(product.id);
|
||||||
|
} else if (!shouldBeInStock && !currentlyOutOfStock) {
|
||||||
|
// Should be out of stock but currently in stock - needs change
|
||||||
|
toMarkOutOfStock.push(product.id);
|
||||||
|
changed.push(product.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Batch update products in a single query
|
||||||
|
if (toMarkInStock.length > 0) {
|
||||||
|
await db.update(productInfo)
|
||||||
|
.set({ isOutOfStock: false })
|
||||||
|
.where(inArray(productInfo.id, toMarkInStock));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (toMarkOutOfStock.length > 0) {
|
||||||
|
await db.update(productInfo)
|
||||||
|
.set({ isOutOfStock: true })
|
||||||
|
.where(inArray(productInfo.id, toMarkOutOfStock));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reinitialize stores if any products changed
|
||||||
|
if (changed.length > 0 && reInitialize) {
|
||||||
|
console.log(`Reinitializing stores after availability changes for ${changed.length} products`);
|
||||||
|
await initializeAllStores();
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
inStock: computedInStock,
|
||||||
|
outOfStock: computedOutOfStock,
|
||||||
|
changed
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -6,7 +6,8 @@ import { initializeSlotStore } from '@/src/stores/slot-store'
|
||||||
import { initializeBannerStore } from '@/src/stores/banner-store'
|
import { initializeBannerStore } from '@/src/stores/banner-store'
|
||||||
import { createAllCacheFiles } from '@/src/lib/cloud_cache'
|
import { createAllCacheFiles } from '@/src/lib/cloud_cache'
|
||||||
|
|
||||||
const STORE_INIT_DELAY_MS = 3 * 60 * 1000
|
// const STORE_INIT_DELAY_MS = 3 * 60 * 1000
|
||||||
|
const STORE_INIT_DELAY_MS = 0.5 * 60 * 1000
|
||||||
let storeInitializationTimeout: NodeJS.Timeout | null = null
|
let storeInitializationTimeout: NodeJS.Timeout | null = null
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ import addressRouter from '@/src/trpc/apis/admin-apis/apis/address'
|
||||||
import { bannerRouter } from '@/src/trpc/apis/admin-apis/apis/banner'
|
import { bannerRouter } from '@/src/trpc/apis/admin-apis/apis/banner'
|
||||||
import { userRouter } from '@/src/trpc/apis/admin-apis/apis/user'
|
import { userRouter } from '@/src/trpc/apis/admin-apis/apis/user'
|
||||||
import { constRouter } from '@/src/trpc/apis/admin-apis/apis/const'
|
import { constRouter } from '@/src/trpc/apis/admin-apis/apis/const'
|
||||||
|
import { productAvailabilitySchedulesRouter } from '@/src/trpc/apis/admin-apis/apis/product-availability-schedules'
|
||||||
|
|
||||||
export const adminRouter = router({
|
export const adminRouter = router({
|
||||||
complaint: complaintRouter,
|
complaint: complaintRouter,
|
||||||
|
|
@ -30,6 +31,7 @@ export const adminRouter = router({
|
||||||
banner: bannerRouter,
|
banner: bannerRouter,
|
||||||
user: userRouter,
|
user: userRouter,
|
||||||
const: constRouter,
|
const: constRouter,
|
||||||
|
productAvailabilitySchedules: productAvailabilitySchedulesRouter,
|
||||||
});
|
});
|
||||||
|
|
||||||
export type AdminRouter = typeof adminRouter;
|
export type AdminRouter = typeof adminRouter;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,154 @@
|
||||||
|
import { router, protectedProcedure } from '@/src/trpc/trpc-index'
|
||||||
|
import { z } from 'zod';
|
||||||
|
import { db } from '@/src/db/db_index'
|
||||||
|
import { productAvailabilitySchedules } from '@/src/db/schema'
|
||||||
|
import { eq } from 'drizzle-orm';
|
||||||
|
import { refreshScheduleJobs } from '@/src/lib/automatedJobs';
|
||||||
|
|
||||||
|
const createScheduleSchema = z.object({
|
||||||
|
scheduleName: z.string().min(1, "Schedule name is required"),
|
||||||
|
time: z.string().min(1, "Time is required").regex(/^([01]?[0-9]|2[0-3]):[0-5][0-9]$/, "Invalid time format. Use HH:MM"),
|
||||||
|
action: z.enum(['in', 'out']),
|
||||||
|
productIds: z.array(z.number().int().positive()).min(1, "At least one product is required"),
|
||||||
|
groupIds: z.array(z.number().int().positive()).default([]),
|
||||||
|
});
|
||||||
|
|
||||||
|
const updateScheduleSchema = z.object({
|
||||||
|
id: z.number().int().positive(),
|
||||||
|
updates: createScheduleSchema.partial().extend({
|
||||||
|
scheduleName: z.string().min(1).optional(),
|
||||||
|
productIds: z.array(z.number().int().positive()).optional(),
|
||||||
|
groupIds: z.array(z.number().int().positive()).optional(),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const productAvailabilitySchedulesRouter = router({
|
||||||
|
create: protectedProcedure
|
||||||
|
.input(createScheduleSchema)
|
||||||
|
.mutation(async ({ input, ctx }) => {
|
||||||
|
const { scheduleName, time, action, productIds, groupIds } = input;
|
||||||
|
|
||||||
|
// Get staff user ID from auth middleware
|
||||||
|
const staffUserId = ctx.staffUser?.id;
|
||||||
|
if (!staffUserId) {
|
||||||
|
throw new Error("Unauthorized");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if schedule name already exists
|
||||||
|
const existingSchedule = await db.query.productAvailabilitySchedules.findFirst({
|
||||||
|
where: eq(productAvailabilitySchedules.scheduleName, scheduleName),
|
||||||
|
});
|
||||||
|
if (existingSchedule) {
|
||||||
|
throw new Error("Schedule name already exists");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create schedule with arrays
|
||||||
|
const scheduleResult = await db.insert(productAvailabilitySchedules).values({
|
||||||
|
scheduleName,
|
||||||
|
time,
|
||||||
|
action,
|
||||||
|
productIds,
|
||||||
|
groupIds,
|
||||||
|
}).returning();
|
||||||
|
|
||||||
|
// Refresh cron jobs to include new schedule
|
||||||
|
await refreshScheduleJobs();
|
||||||
|
|
||||||
|
return scheduleResult[0];
|
||||||
|
}),
|
||||||
|
|
||||||
|
getAll: protectedProcedure
|
||||||
|
.query(async () => {
|
||||||
|
const schedules = await db.query.productAvailabilitySchedules.findMany({
|
||||||
|
orderBy: (productAvailabilitySchedules, { desc }) => [desc(productAvailabilitySchedules.createdAt)],
|
||||||
|
});
|
||||||
|
|
||||||
|
return schedules.map(schedule => ({
|
||||||
|
...schedule,
|
||||||
|
productCount: schedule.productIds.length,
|
||||||
|
groupCount: schedule.groupIds.length,
|
||||||
|
}));
|
||||||
|
}),
|
||||||
|
|
||||||
|
getById: protectedProcedure
|
||||||
|
.input(z.object({ id: z.number().int().positive() }))
|
||||||
|
.query(async ({ input }) => {
|
||||||
|
const { id } = input;
|
||||||
|
|
||||||
|
const schedule = await db.query.productAvailabilitySchedules.findFirst({
|
||||||
|
where: eq(productAvailabilitySchedules.id, id),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!schedule) {
|
||||||
|
throw new Error("Schedule not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
return schedule;
|
||||||
|
}),
|
||||||
|
|
||||||
|
update: protectedProcedure
|
||||||
|
.input(updateScheduleSchema)
|
||||||
|
.mutation(async ({ input }) => {
|
||||||
|
const { id, updates } = input;
|
||||||
|
|
||||||
|
// Check if schedule exists
|
||||||
|
const existingSchedule = await db.query.productAvailabilitySchedules.findFirst({
|
||||||
|
where: eq(productAvailabilitySchedules.id, id),
|
||||||
|
});
|
||||||
|
if (!existingSchedule) {
|
||||||
|
throw new Error("Schedule not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check schedule name uniqueness if being updated
|
||||||
|
if (updates.scheduleName && updates.scheduleName !== existingSchedule.scheduleName) {
|
||||||
|
const duplicateSchedule = await db.query.productAvailabilitySchedules.findFirst({
|
||||||
|
where: eq(productAvailabilitySchedules.scheduleName, updates.scheduleName),
|
||||||
|
});
|
||||||
|
if (duplicateSchedule) {
|
||||||
|
throw new Error("Schedule name already exists");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update schedule
|
||||||
|
const updateData: any = {};
|
||||||
|
if (updates.scheduleName !== undefined) updateData.scheduleName = updates.scheduleName;
|
||||||
|
if (updates.time !== undefined) updateData.time = updates.time;
|
||||||
|
if (updates.action !== undefined) updateData.action = updates.action;
|
||||||
|
if (updates.productIds !== undefined) updateData.productIds = updates.productIds;
|
||||||
|
if (updates.groupIds !== undefined) updateData.groupIds = updates.groupIds;
|
||||||
|
updateData.lastUpdated = new Date();
|
||||||
|
|
||||||
|
const result = await db.update(productAvailabilitySchedules)
|
||||||
|
.set(updateData)
|
||||||
|
.where(eq(productAvailabilitySchedules.id, id))
|
||||||
|
.returning();
|
||||||
|
|
||||||
|
if (result.length === 0) {
|
||||||
|
throw new Error("Failed to update schedule");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Refresh cron jobs to reflect changes
|
||||||
|
await refreshScheduleJobs();
|
||||||
|
|
||||||
|
return result[0];
|
||||||
|
}),
|
||||||
|
|
||||||
|
delete: protectedProcedure
|
||||||
|
.input(z.object({ id: z.number().int().positive() }))
|
||||||
|
.mutation(async ({ input }) => {
|
||||||
|
const { id } = input;
|
||||||
|
|
||||||
|
const result = await db.delete(productAvailabilitySchedules)
|
||||||
|
.where(eq(productAvailabilitySchedules.id, id))
|
||||||
|
.returning();
|
||||||
|
|
||||||
|
if (result.length === 0) {
|
||||||
|
throw new Error("Schedule not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Refresh cron jobs to remove deleted schedule
|
||||||
|
await refreshScheduleJobs();
|
||||||
|
|
||||||
|
return { message: "Schedule deleted successfully" };
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
@ -16,23 +16,26 @@ const polygon = turf.polygon(mbnrGeoJson.features[0].geometry.coordinates);
|
||||||
export async function scaffoldEssentialConsts() {
|
export async function scaffoldEssentialConsts() {
|
||||||
const consts = await getAllConstValues();
|
const consts = await getAllConstValues();
|
||||||
|
|
||||||
return {
|
|
||||||
freeDeliveryThreshold: consts[CONST_KEYS.freeDeliveryThreshold] ?? 200,
|
return {
|
||||||
deliveryCharge: consts[CONST_KEYS.deliveryCharge] ?? 0,
|
freeDeliveryThreshold: consts[CONST_KEYS.freeDeliveryThreshold] ?? 200,
|
||||||
flashFreeDeliveryThreshold: consts[CONST_KEYS.flashFreeDeliveryThreshold] ?? 500,
|
deliveryCharge: consts[CONST_KEYS.deliveryCharge] ?? 0,
|
||||||
flashDeliveryCharge: consts[CONST_KEYS.flashDeliveryCharge] ?? 69,
|
flashFreeDeliveryThreshold: consts[CONST_KEYS.flashFreeDeliveryThreshold] ?? 500,
|
||||||
popularItems: consts[CONST_KEYS.popularItems] ?? '5,3,2,4,1',
|
flashDeliveryCharge: consts[CONST_KEYS.flashDeliveryCharge] ?? 69,
|
||||||
versionNum: consts[CONST_KEYS.versionNum] ?? '1.1.0',
|
popularItems: consts[CONST_KEYS.popularItems] ?? '5,3,2,4,1',
|
||||||
playStoreUrl: consts[CONST_KEYS.playStoreUrl] ?? 'https://play.google.com/store/apps/details?id=in.freshyo.app',
|
versionNum: consts[CONST_KEYS.versionNum] ?? '1.1.0',
|
||||||
appStoreUrl: consts[CONST_KEYS.appStoreUrl] ?? 'https://apps.apple.com/in/app/freshyo/id6756889077',
|
playStoreUrl: consts[CONST_KEYS.playStoreUrl] ?? 'https://play.google.com/store/apps/details?id=in.freshyo.app',
|
||||||
webViewHtml: null,
|
appStoreUrl: consts[CONST_KEYS.appStoreUrl] ?? 'https://apps.apple.com/in/app/freshyo/id6756889077',
|
||||||
isWebviewClosable: true,
|
webviewHtml: `<div style="display: flex; flex-direction: column; align-items: center; justify-content: center; height: 100%;">
|
||||||
isFlashDeliveryEnabled: consts[CONST_KEYS.isFlashDeliveryEnabled] ?? true,
|
<h1 style="font-size: 72px">You're using an old version of app. please restart to update. Just close and open</h1>
|
||||||
supportMobile: consts[CONST_KEYS.supportMobile] ?? '',
|
<h1 style="font-size: 36px; margin-top: 48px">With love, from Freshyo</h5>
|
||||||
supportEmail: consts[CONST_KEYS.supportEmail] ?? '',
|
</div>`,
|
||||||
assetsDomain,
|
isWebviewClosable: false,
|
||||||
apiCacheKey,
|
isFlashDeliveryEnabled: consts[CONST_KEYS.isFlashDeliveryEnabled] ?? true,
|
||||||
};
|
supportMobile: consts[CONST_KEYS.supportMobile] ?? '',
|
||||||
|
supportEmail: consts[CONST_KEYS.supportEmail] ?? '',
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const commonApiRouter = router({
|
export const commonApiRouter = router({
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,8 @@
|
||||||
},
|
},
|
||||||
"build": {
|
"build": {
|
||||||
"development": {
|
"development": {
|
||||||
"developmentClient": true,
|
|
||||||
"distribution": "internal",
|
"distribution": "internal",
|
||||||
|
"autoIncrement": true,
|
||||||
"channel": "development"
|
"channel": "development"
|
||||||
},
|
},
|
||||||
"preview": {
|
"preview": {
|
||||||
|
|
|
||||||
|
|
@ -64,9 +64,9 @@ const isDevMode = Constants.executionEnvironment !== "standalone";
|
||||||
// const BASE_API_URL = 'http://10.0.2.2:4000';
|
// const BASE_API_URL = 'http://10.0.2.2:4000';
|
||||||
// const BASE_API_URL = 'http://192.168.100.101:4000';
|
// const BASE_API_URL = 'http://192.168.100.101:4000';
|
||||||
// const BASE_API_URL = 'http://192.168.1.5:4000';
|
// const BASE_API_URL = 'http://192.168.1.5:4000';
|
||||||
let BASE_API_URL = "https://mf.freshyo.in";
|
// let BASE_API_URL = "https://mf.freshyo.in";
|
||||||
// let BASE_API_URL = "https://freshyo.technocracy.ovh";
|
let BASE_API_URL = "https://freshyo.technocracy.ovh";
|
||||||
// let BASE_API_URL = 'http://192.168.100.107:4000';
|
// let BASE_API_URL = 'http://192.168.100.108:4000';
|
||||||
// let BASE_API_URL = 'http://192.168.29.176:4000';
|
// let BASE_API_URL = 'http://192.168.29.176:4000';
|
||||||
|
|
||||||
// if(isDevMode) {
|
// if(isDevMode) {
|
||||||
|
|
|
||||||
39
test/.detoxrc.json
Normal file
39
test/.detoxrc.json
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
{
|
||||||
|
"testRunner": {
|
||||||
|
"args": {
|
||||||
|
"$0": "jest",
|
||||||
|
"config": "jest.config.cjs"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"apps": {
|
||||||
|
"admin.ios": {
|
||||||
|
"type": "ios.app",
|
||||||
|
"binaryPath": "appBinaries/admin.app",
|
||||||
|
"bundleId": "in.freshyo.adminui"
|
||||||
|
},
|
||||||
|
"user.ios": {
|
||||||
|
"type": "ios.app",
|
||||||
|
"binaryPath": "appBinaries/user.app",
|
||||||
|
"bundleId": "com.freshyotrial.app"
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
"devices": {
|
||||||
|
"simulator": {
|
||||||
|
"type": "ios.simulator",
|
||||||
|
"device": {
|
||||||
|
"type": "iPhone 16 Pro Max"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"configurations": {
|
||||||
|
"ios.user": {
|
||||||
|
"device": "simulator",
|
||||||
|
"app": "user.ios"
|
||||||
|
},
|
||||||
|
"ios.admin": {
|
||||||
|
"device": "simulator",
|
||||||
|
"app": "admin.ios"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
34
test/.gitignore
vendored
Normal file
34
test/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
# dependencies (bun install)
|
||||||
|
node_modules
|
||||||
|
|
||||||
|
# output
|
||||||
|
out
|
||||||
|
dist
|
||||||
|
*.tgz
|
||||||
|
|
||||||
|
# code coverage
|
||||||
|
coverage
|
||||||
|
*.lcov
|
||||||
|
|
||||||
|
# logs
|
||||||
|
logs
|
||||||
|
_.log
|
||||||
|
report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json
|
||||||
|
|
||||||
|
# dotenv environment variable files
|
||||||
|
.env
|
||||||
|
.env.development.local
|
||||||
|
.env.test.local
|
||||||
|
.env.production.local
|
||||||
|
.env.local
|
||||||
|
|
||||||
|
# caches
|
||||||
|
.eslintcache
|
||||||
|
.cache
|
||||||
|
*.tsbuildinfo
|
||||||
|
|
||||||
|
# IntelliJ based IDEs
|
||||||
|
.idea
|
||||||
|
|
||||||
|
# Finder (MacOS) folder config
|
||||||
|
.DS_Store
|
||||||
BIN
test/Freshyo.app/AppAuthCore_Privacy.bundle/Info.plist
Normal file
BIN
test/Freshyo.app/AppAuthCore_Privacy.bundle/Info.plist
Normal file
Binary file not shown.
|
|
@ -0,0 +1,16 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>NSPrivacyAccessedAPITypes</key>
|
||||||
|
<array>
|
||||||
|
</array>
|
||||||
|
<key>NSPrivacyCollectedDataTypes</key>
|
||||||
|
<array>
|
||||||
|
</array>
|
||||||
|
<key>NSPrivacyTrackingDomains</key>
|
||||||
|
<array/>
|
||||||
|
<key>NSPrivacyTracking</key>
|
||||||
|
<false/>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
BIN
test/Freshyo.app/AppIcon60x60@2x.png
Normal file
BIN
test/Freshyo.app/AppIcon60x60@2x.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 16 KiB |
BIN
test/Freshyo.app/AppIcon76x76@2x~ipad.png
Normal file
BIN
test/Freshyo.app/AppIcon76x76@2x~ipad.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 24 KiB |
BIN
test/Freshyo.app/Assets.car
Normal file
BIN
test/Freshyo.app/Assets.car
Normal file
Binary file not shown.
BIN
test/Freshyo.app/EXConstants.bundle/Info.plist
Normal file
BIN
test/Freshyo.app/EXConstants.bundle/Info.plist
Normal file
Binary file not shown.
1
test/Freshyo.app/EXConstants.bundle/app.config
Normal file
1
test/Freshyo.app/EXConstants.bundle/app.config
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
{"name":"Freshyo","slug":"freshyo","version":"1.2.0","orientation":"portrait","icon":"./assets/images/freshyo-logo.png","scheme":"freshyo","userInterfaceStyle":"automatic","newArchEnabled":true,"ios":{"buildNumber":"1.1.0","supportsTablet":true,"bundleIdentifier":"com.freshyotrial.app","infoPlist":{"LSApplicationQueriesSchemes":["ppemerchantsdkv1","ppemerchantsdkv2","ppemerchantsdkv3","paytmmp","gpay","ppemerchantsdkv1","ppemerchantsdkv2","ppemerchantsdkv3","paytmmp","gpay","ppemerchantsdkv1","ppemerchantsdkv2","ppemerchantsdkv3","paytmmp","gpay","ppemerchantsdkv1","ppemerchantsdkv2","ppemerchantsdkv3","paytmmp","gpay","ppemerchantsdkv1","ppemerchantsdkv2","ppemerchantsdkv3","paytmmp","gpay","ppemerchantsdkv1","ppemerchantsdkv2","ppemerchantsdkv3","paytmmp","gpay","ppemerchantsdkv1","ppemerchantsdkv2","ppemerchantsdkv3","paytmmp","gpay","ppemerchantsdkv1","ppemerchantsdkv2","ppemerchantsdkv3","paytmmp","gpay"],"ITSAppUsesNonExemptEncryption":false,"NSPhotoLibraryUsageDescription":"This app uses photo library to allow users to upload pictures as a part or product's review and feedback.","NSLocationWhenInUseUsageDescription":"This app uses your location to decide if your place is serviceable or not."}},"android":{"softwareKeyboardLayoutMode":"resize","adaptiveIcon":{"foregroundImage":"./assets/images/freshyo-logo.png","backgroundColor":"#fff0f6"},"edgeToEdgeEnabled":true,"package":"in.freshyo.app","googleServicesFile":"./google-services.json"},"web":{"bundler":"metro","output":"static","favicon":"./assets/images/favicon.png"},"plugins":["expo-router",["expo-splash-screen",{"image":"./assets/images/freshyo-logo.png","imageWidth":200,"resizeMode":"contain","backgroundColor":"#ffffff"}],"expo-secure-store","expo-notifications"],"experiments":{"typedRoutes":true},"extra":{"router":{},"eas":{"projectId":"7f3e7611-f7a8-45f9-8a99-c2b1f6454d48"}},"runtimeVersion":{"policy":"appVersion"},"updates":{"url":"https://u.expo.dev/7f3e7611-f7a8-45f9-8a99-c2b1f6454d48"},"owner":"mohammedshafiuddin54","sdkVersion":"53.0.0","platforms":["ios","android","web"],"androidStatusBar":{"backgroundColor":"#ffffff"}}
|
||||||
BIN
test/Freshyo.app/EXUpdates.bundle/Info.plist
Normal file
BIN
test/Freshyo.app/EXUpdates.bundle/Info.plist
Normal file
Binary file not shown.
BIN
test/Freshyo.app/Expo.plist
Normal file
BIN
test/Freshyo.app/Expo.plist
Normal file
Binary file not shown.
BIN
test/Freshyo.app/ExpoApplication_privacy.bundle/Info.plist
Normal file
BIN
test/Freshyo.app/ExpoApplication_privacy.bundle/Info.plist
Normal file
Binary file not shown.
|
|
@ -0,0 +1,24 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>NSPrivacyCollectedDataTypes</key>
|
||||||
|
<array>
|
||||||
|
</array>
|
||||||
|
<key>NSPrivacyTracking</key>
|
||||||
|
<false/>
|
||||||
|
<key>NSPrivacyTrackingDomains</key>
|
||||||
|
<array/>
|
||||||
|
<key>NSPrivacyAccessedAPITypes</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>NSPrivacyAccessedAPIType</key>
|
||||||
|
<string>NSPrivacyAccessedAPICategoryFileTimestamp</string>
|
||||||
|
<key>NSPrivacyAccessedAPITypeReasons</key>
|
||||||
|
<array>
|
||||||
|
<string>C617.1</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
BIN
test/Freshyo.app/ExpoConstants_privacy.bundle/Info.plist
Normal file
BIN
test/Freshyo.app/ExpoConstants_privacy.bundle/Info.plist
Normal file
Binary file not shown.
|
|
@ -0,0 +1,24 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>NSPrivacyCollectedDataTypes</key>
|
||||||
|
<array>
|
||||||
|
</array>
|
||||||
|
<key>NSPrivacyTracking</key>
|
||||||
|
<false/>
|
||||||
|
<key>NSPrivacyTrackingDomains</key>
|
||||||
|
<array/>
|
||||||
|
<key>NSPrivacyAccessedAPITypes</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>NSPrivacyAccessedAPIType</key>
|
||||||
|
<string>NSPrivacyAccessedAPICategoryUserDefaults</string>
|
||||||
|
<key>NSPrivacyAccessedAPITypeReasons</key>
|
||||||
|
<array>
|
||||||
|
<string>CA92.1</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
BIN
test/Freshyo.app/ExpoDevice_privacy.bundle/Info.plist
Normal file
BIN
test/Freshyo.app/ExpoDevice_privacy.bundle/Info.plist
Normal file
Binary file not shown.
|
|
@ -0,0 +1,24 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>NSPrivacyCollectedDataTypes</key>
|
||||||
|
<array>
|
||||||
|
</array>
|
||||||
|
<key>NSPrivacyTracking</key>
|
||||||
|
<false/>
|
||||||
|
<key>NSPrivacyTrackingDomains</key>
|
||||||
|
<array/>
|
||||||
|
<key>NSPrivacyAccessedAPITypes</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>NSPrivacyAccessedAPIType</key>
|
||||||
|
<string>NSPrivacyAccessedAPICategorySystemBootTime</string>
|
||||||
|
<key>NSPrivacyAccessedAPITypeReasons</key>
|
||||||
|
<array>
|
||||||
|
<string>35F9.1</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
BIN
test/Freshyo.app/ExpoFileSystem_privacy.bundle/Info.plist
Normal file
BIN
test/Freshyo.app/ExpoFileSystem_privacy.bundle/Info.plist
Normal file
Binary file not shown.
|
|
@ -0,0 +1,34 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>NSPrivacyCollectedDataTypes</key>
|
||||||
|
<array>
|
||||||
|
</array>
|
||||||
|
<key>NSPrivacyTracking</key>
|
||||||
|
<false/>
|
||||||
|
<key>NSPrivacyTrackingDomains</key>
|
||||||
|
<array/>
|
||||||
|
<key>NSPrivacyAccessedAPITypes</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>NSPrivacyAccessedAPIType</key>
|
||||||
|
<string>NSPrivacyAccessedAPICategoryFileTimestamp</string>
|
||||||
|
<key>NSPrivacyAccessedAPITypeReasons</key>
|
||||||
|
<array>
|
||||||
|
<string>0A2A.1</string>
|
||||||
|
<string>3B52.1</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>NSPrivacyAccessedAPIType</key>
|
||||||
|
<string>NSPrivacyAccessedAPICategoryDiskSpace</string>
|
||||||
|
<key>NSPrivacyAccessedAPITypeReasons</key>
|
||||||
|
<array>
|
||||||
|
<string>E174.1</string>
|
||||||
|
<string>85F4.1</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
BIN
test/Freshyo.app/ExpoNotifications_privacy.bundle/Info.plist
Normal file
BIN
test/Freshyo.app/ExpoNotifications_privacy.bundle/Info.plist
Normal file
Binary file not shown.
|
|
@ -0,0 +1,24 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>NSPrivacyCollectedDataTypes</key>
|
||||||
|
<array>
|
||||||
|
</array>
|
||||||
|
<key>NSPrivacyTracking</key>
|
||||||
|
<false/>
|
||||||
|
<key>NSPrivacyTrackingDomains</key>
|
||||||
|
<array/>
|
||||||
|
<key>NSPrivacyAccessedAPITypes</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>NSPrivacyAccessedAPIType</key>
|
||||||
|
<string>NSPrivacyAccessedAPICategoryUserDefaults</string>
|
||||||
|
<key>NSPrivacyAccessedAPITypeReasons</key>
|
||||||
|
<array>
|
||||||
|
<string>CA92.1</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
BIN
test/Freshyo.app/ExpoSystemUI_privacy.bundle/Info.plist
Normal file
BIN
test/Freshyo.app/ExpoSystemUI_privacy.bundle/Info.plist
Normal file
Binary file not shown.
|
|
@ -0,0 +1,24 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>NSPrivacyCollectedDataTypes</key>
|
||||||
|
<array>
|
||||||
|
</array>
|
||||||
|
<key>NSPrivacyTracking</key>
|
||||||
|
<false/>
|
||||||
|
<key>NSPrivacyTrackingDomains</key>
|
||||||
|
<array/>
|
||||||
|
<key>NSPrivacyAccessedAPITypes</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>NSPrivacyAccessedAPIType</key>
|
||||||
|
<string>NSPrivacyAccessedAPICategoryUserDefaults</string>
|
||||||
|
<key>NSPrivacyAccessedAPITypeReasons</key>
|
||||||
|
<array>
|
||||||
|
<string>CA92.1</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
BIN
test/Freshyo.app/FBLPromises_Privacy.bundle/Info.plist
Normal file
BIN
test/Freshyo.app/FBLPromises_Privacy.bundle/Info.plist
Normal file
Binary file not shown.
|
|
@ -0,0 +1,14 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>NSPrivacyAccessedAPITypes</key>
|
||||||
|
<array/>
|
||||||
|
<key>NSPrivacyCollectedDataTypes</key>
|
||||||
|
<array/>
|
||||||
|
<key>NSPrivacyTracking</key>
|
||||||
|
<false/>
|
||||||
|
<key>NSPrivacyTrackingDomains</key>
|
||||||
|
<array/>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
BIN
test/Freshyo.app/Frameworks/Razorpay.framework/Info.plist
Normal file
BIN
test/Freshyo.app/Frameworks/Razorpay.framework/Info.plist
Normal file
Binary file not shown.
BIN
test/Freshyo.app/Frameworks/Razorpay.framework/Razorpay
Executable file
BIN
test/Freshyo.app/Frameworks/Razorpay.framework/Razorpay
Executable file
Binary file not shown.
|
|
@ -0,0 +1,101 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>files</key>
|
||||||
|
<dict>
|
||||||
|
<key>Info.plist</key>
|
||||||
|
<data>
|
||||||
|
m2+OcUVS+NCF/RaQ0NMeexAJIbU=
|
||||||
|
</data>
|
||||||
|
</dict>
|
||||||
|
<key>files2</key>
|
||||||
|
<dict/>
|
||||||
|
<key>rules</key>
|
||||||
|
<dict>
|
||||||
|
<key>^.*</key>
|
||||||
|
<true/>
|
||||||
|
<key>^.*\.lproj/</key>
|
||||||
|
<dict>
|
||||||
|
<key>optional</key>
|
||||||
|
<true/>
|
||||||
|
<key>weight</key>
|
||||||
|
<real>1000</real>
|
||||||
|
</dict>
|
||||||
|
<key>^.*\.lproj/locversion.plist$</key>
|
||||||
|
<dict>
|
||||||
|
<key>omit</key>
|
||||||
|
<true/>
|
||||||
|
<key>weight</key>
|
||||||
|
<real>1100</real>
|
||||||
|
</dict>
|
||||||
|
<key>^Base\.lproj/</key>
|
||||||
|
<dict>
|
||||||
|
<key>weight</key>
|
||||||
|
<real>1010</real>
|
||||||
|
</dict>
|
||||||
|
<key>^version.plist$</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
<key>rules2</key>
|
||||||
|
<dict>
|
||||||
|
<key>.*\.dSYM($|/)</key>
|
||||||
|
<dict>
|
||||||
|
<key>weight</key>
|
||||||
|
<real>11</real>
|
||||||
|
</dict>
|
||||||
|
<key>^(.*/)?\.DS_Store$</key>
|
||||||
|
<dict>
|
||||||
|
<key>omit</key>
|
||||||
|
<true/>
|
||||||
|
<key>weight</key>
|
||||||
|
<real>2000</real>
|
||||||
|
</dict>
|
||||||
|
<key>^.*</key>
|
||||||
|
<true/>
|
||||||
|
<key>^.*\.lproj/</key>
|
||||||
|
<dict>
|
||||||
|
<key>optional</key>
|
||||||
|
<true/>
|
||||||
|
<key>weight</key>
|
||||||
|
<real>1000</real>
|
||||||
|
</dict>
|
||||||
|
<key>^.*\.lproj/locversion.plist$</key>
|
||||||
|
<dict>
|
||||||
|
<key>omit</key>
|
||||||
|
<true/>
|
||||||
|
<key>weight</key>
|
||||||
|
<real>1100</real>
|
||||||
|
</dict>
|
||||||
|
<key>^Base\.lproj/</key>
|
||||||
|
<dict>
|
||||||
|
<key>weight</key>
|
||||||
|
<real>1010</real>
|
||||||
|
</dict>
|
||||||
|
<key>^Info\.plist$</key>
|
||||||
|
<dict>
|
||||||
|
<key>omit</key>
|
||||||
|
<true/>
|
||||||
|
<key>weight</key>
|
||||||
|
<real>20</real>
|
||||||
|
</dict>
|
||||||
|
<key>^PkgInfo$</key>
|
||||||
|
<dict>
|
||||||
|
<key>omit</key>
|
||||||
|
<true/>
|
||||||
|
<key>weight</key>
|
||||||
|
<real>20</real>
|
||||||
|
</dict>
|
||||||
|
<key>^embedded\.provisionprofile$</key>
|
||||||
|
<dict>
|
||||||
|
<key>weight</key>
|
||||||
|
<real>20</real>
|
||||||
|
</dict>
|
||||||
|
<key>^version\.plist$</key>
|
||||||
|
<dict>
|
||||||
|
<key>weight</key>
|
||||||
|
<real>20</real>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
BIN
test/Freshyo.app/Frameworks/RazorpayCore.framework/Info.plist
Normal file
BIN
test/Freshyo.app/Frameworks/RazorpayCore.framework/Info.plist
Normal file
Binary file not shown.
BIN
test/Freshyo.app/Frameworks/RazorpayCore.framework/RazorpayCore
Executable file
BIN
test/Freshyo.app/Frameworks/RazorpayCore.framework/RazorpayCore
Executable file
Binary file not shown.
|
|
@ -0,0 +1,101 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>files</key>
|
||||||
|
<dict>
|
||||||
|
<key>Info.plist</key>
|
||||||
|
<data>
|
||||||
|
IgSq/mUvfi0nncHC2TAKVG0zPwA=
|
||||||
|
</data>
|
||||||
|
</dict>
|
||||||
|
<key>files2</key>
|
||||||
|
<dict/>
|
||||||
|
<key>rules</key>
|
||||||
|
<dict>
|
||||||
|
<key>^.*</key>
|
||||||
|
<true/>
|
||||||
|
<key>^.*\.lproj/</key>
|
||||||
|
<dict>
|
||||||
|
<key>optional</key>
|
||||||
|
<true/>
|
||||||
|
<key>weight</key>
|
||||||
|
<real>1000</real>
|
||||||
|
</dict>
|
||||||
|
<key>^.*\.lproj/locversion.plist$</key>
|
||||||
|
<dict>
|
||||||
|
<key>omit</key>
|
||||||
|
<true/>
|
||||||
|
<key>weight</key>
|
||||||
|
<real>1100</real>
|
||||||
|
</dict>
|
||||||
|
<key>^Base\.lproj/</key>
|
||||||
|
<dict>
|
||||||
|
<key>weight</key>
|
||||||
|
<real>1010</real>
|
||||||
|
</dict>
|
||||||
|
<key>^version.plist$</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
<key>rules2</key>
|
||||||
|
<dict>
|
||||||
|
<key>.*\.dSYM($|/)</key>
|
||||||
|
<dict>
|
||||||
|
<key>weight</key>
|
||||||
|
<real>11</real>
|
||||||
|
</dict>
|
||||||
|
<key>^(.*/)?\.DS_Store$</key>
|
||||||
|
<dict>
|
||||||
|
<key>omit</key>
|
||||||
|
<true/>
|
||||||
|
<key>weight</key>
|
||||||
|
<real>2000</real>
|
||||||
|
</dict>
|
||||||
|
<key>^.*</key>
|
||||||
|
<true/>
|
||||||
|
<key>^.*\.lproj/</key>
|
||||||
|
<dict>
|
||||||
|
<key>optional</key>
|
||||||
|
<true/>
|
||||||
|
<key>weight</key>
|
||||||
|
<real>1000</real>
|
||||||
|
</dict>
|
||||||
|
<key>^.*\.lproj/locversion.plist$</key>
|
||||||
|
<dict>
|
||||||
|
<key>omit</key>
|
||||||
|
<true/>
|
||||||
|
<key>weight</key>
|
||||||
|
<real>1100</real>
|
||||||
|
</dict>
|
||||||
|
<key>^Base\.lproj/</key>
|
||||||
|
<dict>
|
||||||
|
<key>weight</key>
|
||||||
|
<real>1010</real>
|
||||||
|
</dict>
|
||||||
|
<key>^Info\.plist$</key>
|
||||||
|
<dict>
|
||||||
|
<key>omit</key>
|
||||||
|
<true/>
|
||||||
|
<key>weight</key>
|
||||||
|
<real>20</real>
|
||||||
|
</dict>
|
||||||
|
<key>^PkgInfo$</key>
|
||||||
|
<dict>
|
||||||
|
<key>omit</key>
|
||||||
|
<true/>
|
||||||
|
<key>weight</key>
|
||||||
|
<real>20</real>
|
||||||
|
</dict>
|
||||||
|
<key>^embedded\.provisionprofile$</key>
|
||||||
|
<dict>
|
||||||
|
<key>weight</key>
|
||||||
|
<real>20</real>
|
||||||
|
</dict>
|
||||||
|
<key>^version\.plist$</key>
|
||||||
|
<dict>
|
||||||
|
<key>weight</key>
|
||||||
|
<real>20</real>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 74 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 649 B |
Binary file not shown.
|
After Width: | Height: | Size: 496 B |
File diff suppressed because one or more lines are too long
|
|
@ -0,0 +1 @@
|
||||||
|
12bd975b82f23eb997a3ee2df03e4e33
|
||||||
Binary file not shown.
|
|
@ -0,0 +1,150 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<!--
|
||||||
|
PrivacyInfo.xcprivacy
|
||||||
|
RazorpayIOS
|
||||||
|
|
||||||
|
Created by Vivek Rajesh Shindhe on 18/04/24.
|
||||||
|
Copyright (c) 2024 Razorpay. All rights reserved.
|
||||||
|
-->
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>NSPrivacyAccessedAPITypes</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>NSPrivacyAccessedAPIType</key>
|
||||||
|
<string>NSPrivacyAccessedAPICategoryUserDefaults</string>
|
||||||
|
<key>NSPrivacyAccessedAPITypeReasons</key>
|
||||||
|
<array>
|
||||||
|
<string>CA92.1</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
|
<key>NSPrivacyCollectedDataTypes</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>NSPrivacyCollectedDataType</key>
|
||||||
|
<string>NSPrivacyCollectedDataTypePhysicalAddress</string>
|
||||||
|
<key>NSPrivacyCollectedDataTypeLinked</key>
|
||||||
|
<true/>
|
||||||
|
<key>NSPrivacyCollectedDataTypeTracking</key>
|
||||||
|
<false/>
|
||||||
|
<key>NSPrivacyCollectedDataTypePurposes</key>
|
||||||
|
<array>
|
||||||
|
<string>NSPrivacyCollectedDataTypePurposeAnalytics</string>
|
||||||
|
<string>NSPrivacyCollectedDataTypePurposeAppFunctionality</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>NSPrivacyCollectedDataType</key>
|
||||||
|
<string>NSPrivacyCollectedDataTypeOtherDiagnosticData</string>
|
||||||
|
<key>NSPrivacyCollectedDataTypeLinked</key>
|
||||||
|
<false/>
|
||||||
|
<key>NSPrivacyCollectedDataTypeTracking</key>
|
||||||
|
<false/>
|
||||||
|
<key>NSPrivacyCollectedDataTypePurposes</key>
|
||||||
|
<array>
|
||||||
|
<string>NSPrivacyCollectedDataTypePurposeAnalytics</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>NSPrivacyCollectedDataType</key>
|
||||||
|
<string>NSPrivacyCollectedDataTypeCrashData</string>
|
||||||
|
<key>NSPrivacyCollectedDataTypeLinked</key>
|
||||||
|
<true/>
|
||||||
|
<key>NSPrivacyCollectedDataTypeTracking</key>
|
||||||
|
<false/>
|
||||||
|
<key>NSPrivacyCollectedDataTypePurposes</key>
|
||||||
|
<array>
|
||||||
|
<string>NSPrivacyCollectedDataTypePurposeAnalytics</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>NSPrivacyCollectedDataType</key>
|
||||||
|
<string>NSPrivacyCollectedDataTypeProductInteraction</string>
|
||||||
|
<key>NSPrivacyCollectedDataTypeLinked</key>
|
||||||
|
<true/>
|
||||||
|
<key>NSPrivacyCollectedDataTypeTracking</key>
|
||||||
|
<false/>
|
||||||
|
<key>NSPrivacyCollectedDataTypePurposes</key>
|
||||||
|
<array>
|
||||||
|
<string>NSPrivacyCollectedDataTypePurposeProductPersonalization</string>
|
||||||
|
<string>NSPrivacyCollectedDataTypePurposeAppFunctionality</string>
|
||||||
|
<string>NSPrivacyCollectedDataTypePurposeAnalytics</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>NSPrivacyCollectedDataType</key>
|
||||||
|
<string>NSPrivacyCollectedDataTypeDeviceID</string>
|
||||||
|
<key>NSPrivacyCollectedDataTypeLinked</key>
|
||||||
|
<true/>
|
||||||
|
<key>NSPrivacyCollectedDataTypeTracking</key>
|
||||||
|
<false/>
|
||||||
|
<key>NSPrivacyCollectedDataTypePurposes</key>
|
||||||
|
<array>
|
||||||
|
<string>NSPrivacyCollectedDataTypePurposeAnalytics</string>
|
||||||
|
<string>NSPrivacyCollectedDataTypePurposeProductPersonalization</string>
|
||||||
|
<string>NSPrivacyCollectedDataTypePurposeAppFunctionality</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>NSPrivacyCollectedDataType</key>
|
||||||
|
<string>NSPrivacyCollectedDataTypePaymentInfo</string>
|
||||||
|
<key>NSPrivacyCollectedDataTypeLinked</key>
|
||||||
|
<true/>
|
||||||
|
<key>NSPrivacyCollectedDataTypeTracking</key>
|
||||||
|
<false/>
|
||||||
|
<key>NSPrivacyCollectedDataTypePurposes</key>
|
||||||
|
<array>
|
||||||
|
<string>NSPrivacyCollectedDataTypePurposeAppFunctionality</string>
|
||||||
|
<string>NSPrivacyCollectedDataTypePurposeAnalytics</string>
|
||||||
|
<string>NSPrivacyCollectedDataTypePurposeProductPersonalization</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>NSPrivacyCollectedDataType</key>
|
||||||
|
<string>NSPrivacyCollectedDataTypePhoneNumber</string>
|
||||||
|
<key>NSPrivacyCollectedDataTypeLinked</key>
|
||||||
|
<true/>
|
||||||
|
<key>NSPrivacyCollectedDataTypeTracking</key>
|
||||||
|
<false/>
|
||||||
|
<key>NSPrivacyCollectedDataTypePurposes</key>
|
||||||
|
<array>
|
||||||
|
<string>NSPrivacyCollectedDataTypePurposeAnalytics</string>
|
||||||
|
<string>NSPrivacyCollectedDataTypePurposeAppFunctionality</string>
|
||||||
|
<string>NSPrivacyCollectedDataTypePurposeProductPersonalization</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>NSPrivacyCollectedDataType</key>
|
||||||
|
<string>NSPrivacyCollectedDataTypeName</string>
|
||||||
|
<key>NSPrivacyCollectedDataTypeLinked</key>
|
||||||
|
<true/>
|
||||||
|
<key>NSPrivacyCollectedDataTypeTracking</key>
|
||||||
|
<false/>
|
||||||
|
<key>NSPrivacyCollectedDataTypePurposes</key>
|
||||||
|
<array>
|
||||||
|
<string>NSPrivacyCollectedDataTypePurposeProductPersonalization</string>
|
||||||
|
<string>NSPrivacyCollectedDataTypePurposeAppFunctionality</string>
|
||||||
|
<string>NSPrivacyCollectedDataTypePurposeAnalytics</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>NSPrivacyCollectedDataType</key>
|
||||||
|
<string>Email address</string>
|
||||||
|
<key>NSPrivacyCollectedDataTypeLinked</key>
|
||||||
|
<true/>
|
||||||
|
<key>NSPrivacyCollectedDataTypeTracking</key>
|
||||||
|
<false/>
|
||||||
|
<key>NSPrivacyCollectedDataTypePurposes</key>
|
||||||
|
<array>
|
||||||
|
<string>NSPrivacyCollectedDataTypePurposeProductPersonalization</string>
|
||||||
|
<string>NSPrivacyCollectedDataTypePurposeAnalytics</string>
|
||||||
|
<string>NSPrivacyCollectedDataTypePurposeAppFunctionality</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
|
<key>NSPrivacyTracking</key>
|
||||||
|
<false/>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
BIN
test/Freshyo.app/Frameworks/RazorpayStandard.framework/RazorpayStandard
Executable file
BIN
test/Freshyo.app/Frameworks/RazorpayStandard.framework/RazorpayStandard
Executable file
Binary file not shown.
|
|
@ -0,0 +1,366 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>files</key>
|
||||||
|
<dict>
|
||||||
|
<key>Assets.car</key>
|
||||||
|
<data>
|
||||||
|
9EtCg2Ey2Zi4Ua584kuKWTJN01U=
|
||||||
|
</data>
|
||||||
|
<key>Checkout.storyboardc/Info.plist</key>
|
||||||
|
<data>
|
||||||
|
DFcy10+V49NKtjJ4RLpByN10P1k=
|
||||||
|
</data>
|
||||||
|
<key>Checkout.storyboardc/MagicXNavController.nib/objects-11.0+.nib</key>
|
||||||
|
<data>
|
||||||
|
zRbA0UPDWTIRE/KMCArksV/vK2k=
|
||||||
|
</data>
|
||||||
|
<key>Checkout.storyboardc/MagicXNavController.nib/runtime.nib</key>
|
||||||
|
<data>
|
||||||
|
zRbA0UPDWTIRE/KMCArksV/vK2k=
|
||||||
|
</data>
|
||||||
|
<key>Checkout.storyboardc/OpinionatedAlertVC.nib/objects-11.0+.nib</key>
|
||||||
|
<data>
|
||||||
|
0nbduMecK7uoslXovJgcjilOIB8=
|
||||||
|
</data>
|
||||||
|
<key>Checkout.storyboardc/OpinionatedAlertVC.nib/runtime.nib</key>
|
||||||
|
<data>
|
||||||
|
0nbduMecK7uoslXovJgcjilOIB8=
|
||||||
|
</data>
|
||||||
|
<key>Checkout.storyboardc/QhR-ml-Zo4-view-36U-4g-R9b.nib/objects-11.0+.nib</key>
|
||||||
|
<data>
|
||||||
|
c8N/593lOrAw9wwSo2JSseKACUI=
|
||||||
|
</data>
|
||||||
|
<key>Checkout.storyboardc/QhR-ml-Zo4-view-36U-4g-R9b.nib/runtime.nib</key>
|
||||||
|
<data>
|
||||||
|
GAbF07uihY0DE3w15+txJ0RlSNw=
|
||||||
|
</data>
|
||||||
|
<key>Checkout.storyboardc/RBq-mH-fUs-view-vI7-59-shd.nib/objects-11.0+.nib</key>
|
||||||
|
<data>
|
||||||
|
PcD+CIGNT4GNXBw18IEBAYmUl1A=
|
||||||
|
</data>
|
||||||
|
<key>Checkout.storyboardc/RBq-mH-fUs-view-vI7-59-shd.nib/runtime.nib</key>
|
||||||
|
<data>
|
||||||
|
1LYMZo3gzNiKkp3f7Nzh+lgBbPg=
|
||||||
|
</data>
|
||||||
|
<key>Checkout.storyboardc/RazorpayCheckoutVC.nib/objects-11.0+.nib</key>
|
||||||
|
<data>
|
||||||
|
ENBWg/fxi+P7OShg+NGXd0X+mWQ=
|
||||||
|
</data>
|
||||||
|
<key>Checkout.storyboardc/RazorpayCheckoutVC.nib/runtime.nib</key>
|
||||||
|
<data>
|
||||||
|
ENBWg/fxi+P7OShg+NGXd0X+mWQ=
|
||||||
|
</data>
|
||||||
|
<key>Checkout.storyboardc/RazorpayMagicxVC.nib/objects-11.0+.nib</key>
|
||||||
|
<data>
|
||||||
|
Ia0hGvI0wvIaseYa7wMvQf/q438=
|
||||||
|
</data>
|
||||||
|
<key>Checkout.storyboardc/RazorpayMagicxVC.nib/runtime.nib</key>
|
||||||
|
<data>
|
||||||
|
Ia0hGvI0wvIaseYa7wMvQf/q438=
|
||||||
|
</data>
|
||||||
|
<key>Checkout.storyboardc/UINavigationController-ODs-ga-9IN.nib/objects-11.0+.nib</key>
|
||||||
|
<data>
|
||||||
|
09suesko3lo0ihguFnxyvFMqd6g=
|
||||||
|
</data>
|
||||||
|
<key>Checkout.storyboardc/UINavigationController-ODs-ga-9IN.nib/runtime.nib</key>
|
||||||
|
<data>
|
||||||
|
09suesko3lo0ihguFnxyvFMqd6g=
|
||||||
|
</data>
|
||||||
|
<key>Checkout.storyboardc/ytB-xX-zk3-view-vP9-Lh-TPB.nib/objects-11.0+.nib</key>
|
||||||
|
<data>
|
||||||
|
GuaLaOgcxzpvPP6jj7CE6UW1x54=
|
||||||
|
</data>
|
||||||
|
<key>Checkout.storyboardc/ytB-xX-zk3-view-vP9-Lh-TPB.nib/runtime.nib</key>
|
||||||
|
<data>
|
||||||
|
Ucg3HkU3DNFIfnKf6gQlzd/zqTg=
|
||||||
|
</data>
|
||||||
|
<key>CommonAssets/Razorpay_Logo.png</key>
|
||||||
|
<data>
|
||||||
|
C/QPifs1kjcxzxgwUgDFDlLjpRw=
|
||||||
|
</data>
|
||||||
|
<key>CommonAssets/check_mark.png</key>
|
||||||
|
<data>
|
||||||
|
6d4pPz33KoUobYRDPpGmnPiTVMs=
|
||||||
|
</data>
|
||||||
|
<key>CommonAssets/warning.png</key>
|
||||||
|
<data>
|
||||||
|
gxArEMTCcu4a+ueYNB3oMoIh48o=
|
||||||
|
</data>
|
||||||
|
<key>EncryptedOtpelf.js</key>
|
||||||
|
<data>
|
||||||
|
A893KbMpygzZy6/G1xrQkAudMxw=
|
||||||
|
</data>
|
||||||
|
<key>Hash.txt</key>
|
||||||
|
<data>
|
||||||
|
gN8QKnsfFYPlPa6NstmuiJETxJ8=
|
||||||
|
</data>
|
||||||
|
<key>Info.plist</key>
|
||||||
|
<data>
|
||||||
|
acRXxu68VWw8Pnzhk8Ky6zQaWHE=
|
||||||
|
</data>
|
||||||
|
<key>PrivacyInfo.xcprivacy</key>
|
||||||
|
<data>
|
||||||
|
62HpNLqPh8tKsg+iNP/pSbF2S6M=
|
||||||
|
</data>
|
||||||
|
</dict>
|
||||||
|
<key>files2</key>
|
||||||
|
<dict>
|
||||||
|
<key>Assets.car</key>
|
||||||
|
<dict>
|
||||||
|
<key>hash2</key>
|
||||||
|
<data>
|
||||||
|
HcTTFmbApmRduIkc+LudaGbZclscDoJ6VJdjQDMk5z8=
|
||||||
|
</data>
|
||||||
|
</dict>
|
||||||
|
<key>Checkout.storyboardc/Info.plist</key>
|
||||||
|
<dict>
|
||||||
|
<key>hash2</key>
|
||||||
|
<data>
|
||||||
|
XV+Km0uI0aCCx6b8FFBL8ctnAUqg/+iH2HKwpDJJDns=
|
||||||
|
</data>
|
||||||
|
</dict>
|
||||||
|
<key>Checkout.storyboardc/MagicXNavController.nib/objects-11.0+.nib</key>
|
||||||
|
<dict>
|
||||||
|
<key>hash2</key>
|
||||||
|
<data>
|
||||||
|
/FTBuwa4eqDE4oW65Wzg7NRMxsEk6lrbA5gEQvxHMm8=
|
||||||
|
</data>
|
||||||
|
</dict>
|
||||||
|
<key>Checkout.storyboardc/MagicXNavController.nib/runtime.nib</key>
|
||||||
|
<dict>
|
||||||
|
<key>hash2</key>
|
||||||
|
<data>
|
||||||
|
/FTBuwa4eqDE4oW65Wzg7NRMxsEk6lrbA5gEQvxHMm8=
|
||||||
|
</data>
|
||||||
|
</dict>
|
||||||
|
<key>Checkout.storyboardc/OpinionatedAlertVC.nib/objects-11.0+.nib</key>
|
||||||
|
<dict>
|
||||||
|
<key>hash2</key>
|
||||||
|
<data>
|
||||||
|
TB5d1qjY2vLAC2ml/4EBTkBy3xnnLZQe6gxyjAuM7Hs=
|
||||||
|
</data>
|
||||||
|
</dict>
|
||||||
|
<key>Checkout.storyboardc/OpinionatedAlertVC.nib/runtime.nib</key>
|
||||||
|
<dict>
|
||||||
|
<key>hash2</key>
|
||||||
|
<data>
|
||||||
|
TB5d1qjY2vLAC2ml/4EBTkBy3xnnLZQe6gxyjAuM7Hs=
|
||||||
|
</data>
|
||||||
|
</dict>
|
||||||
|
<key>Checkout.storyboardc/QhR-ml-Zo4-view-36U-4g-R9b.nib/objects-11.0+.nib</key>
|
||||||
|
<dict>
|
||||||
|
<key>hash2</key>
|
||||||
|
<data>
|
||||||
|
9gbR1Bca1fy1VmXM6YTr9iMj/H9FiV5FH80kOSCS5cA=
|
||||||
|
</data>
|
||||||
|
</dict>
|
||||||
|
<key>Checkout.storyboardc/QhR-ml-Zo4-view-36U-4g-R9b.nib/runtime.nib</key>
|
||||||
|
<dict>
|
||||||
|
<key>hash2</key>
|
||||||
|
<data>
|
||||||
|
0aP4/XYhE9pTrC1von5mej2B5hZzviQdM4PQj/nojlY=
|
||||||
|
</data>
|
||||||
|
</dict>
|
||||||
|
<key>Checkout.storyboardc/RBq-mH-fUs-view-vI7-59-shd.nib/objects-11.0+.nib</key>
|
||||||
|
<dict>
|
||||||
|
<key>hash2</key>
|
||||||
|
<data>
|
||||||
|
hg8U6PEGkuQ5LmSJ3QWnXPjAHQ9HR+iYMliJQBqIXsA=
|
||||||
|
</data>
|
||||||
|
</dict>
|
||||||
|
<key>Checkout.storyboardc/RBq-mH-fUs-view-vI7-59-shd.nib/runtime.nib</key>
|
||||||
|
<dict>
|
||||||
|
<key>hash2</key>
|
||||||
|
<data>
|
||||||
|
xzfMa2B7p6qD36vy7gfDE7AfItGacEFMLkGs19efnI0=
|
||||||
|
</data>
|
||||||
|
</dict>
|
||||||
|
<key>Checkout.storyboardc/RazorpayCheckoutVC.nib/objects-11.0+.nib</key>
|
||||||
|
<dict>
|
||||||
|
<key>hash2</key>
|
||||||
|
<data>
|
||||||
|
0D+N+GRb1ZrUQlq4dTQIQfubTWQC44SvjHUHVlVbgeg=
|
||||||
|
</data>
|
||||||
|
</dict>
|
||||||
|
<key>Checkout.storyboardc/RazorpayCheckoutVC.nib/runtime.nib</key>
|
||||||
|
<dict>
|
||||||
|
<key>hash2</key>
|
||||||
|
<data>
|
||||||
|
0D+N+GRb1ZrUQlq4dTQIQfubTWQC44SvjHUHVlVbgeg=
|
||||||
|
</data>
|
||||||
|
</dict>
|
||||||
|
<key>Checkout.storyboardc/RazorpayMagicxVC.nib/objects-11.0+.nib</key>
|
||||||
|
<dict>
|
||||||
|
<key>hash2</key>
|
||||||
|
<data>
|
||||||
|
l6Hz4xHGp/8dMGEWk8KuCnewyrV7Y1XGhWysBUgItFY=
|
||||||
|
</data>
|
||||||
|
</dict>
|
||||||
|
<key>Checkout.storyboardc/RazorpayMagicxVC.nib/runtime.nib</key>
|
||||||
|
<dict>
|
||||||
|
<key>hash2</key>
|
||||||
|
<data>
|
||||||
|
l6Hz4xHGp/8dMGEWk8KuCnewyrV7Y1XGhWysBUgItFY=
|
||||||
|
</data>
|
||||||
|
</dict>
|
||||||
|
<key>Checkout.storyboardc/UINavigationController-ODs-ga-9IN.nib/objects-11.0+.nib</key>
|
||||||
|
<dict>
|
||||||
|
<key>hash2</key>
|
||||||
|
<data>
|
||||||
|
bzY2zdca4b/GrxTr/LmNprdhD3SyIBsZG1W2z7TVW28=
|
||||||
|
</data>
|
||||||
|
</dict>
|
||||||
|
<key>Checkout.storyboardc/UINavigationController-ODs-ga-9IN.nib/runtime.nib</key>
|
||||||
|
<dict>
|
||||||
|
<key>hash2</key>
|
||||||
|
<data>
|
||||||
|
bzY2zdca4b/GrxTr/LmNprdhD3SyIBsZG1W2z7TVW28=
|
||||||
|
</data>
|
||||||
|
</dict>
|
||||||
|
<key>Checkout.storyboardc/ytB-xX-zk3-view-vP9-Lh-TPB.nib/objects-11.0+.nib</key>
|
||||||
|
<dict>
|
||||||
|
<key>hash2</key>
|
||||||
|
<data>
|
||||||
|
I8p2+VocnQu/7nTAHxHwPLoZ9J+et+y957CgfHi+MQo=
|
||||||
|
</data>
|
||||||
|
</dict>
|
||||||
|
<key>Checkout.storyboardc/ytB-xX-zk3-view-vP9-Lh-TPB.nib/runtime.nib</key>
|
||||||
|
<dict>
|
||||||
|
<key>hash2</key>
|
||||||
|
<data>
|
||||||
|
h05YMT+1oAA6um16sH5KVB2c6toLm935YPmV2d/ZDU8=
|
||||||
|
</data>
|
||||||
|
</dict>
|
||||||
|
<key>CommonAssets/Razorpay_Logo.png</key>
|
||||||
|
<dict>
|
||||||
|
<key>hash2</key>
|
||||||
|
<data>
|
||||||
|
udRErjoaEwN536HIEl+2sH6KQ0Q2KzlKwLYCkQlBGKE=
|
||||||
|
</data>
|
||||||
|
</dict>
|
||||||
|
<key>CommonAssets/check_mark.png</key>
|
||||||
|
<dict>
|
||||||
|
<key>hash2</key>
|
||||||
|
<data>
|
||||||
|
s+l4gXoMSGUj0xR2eSdXwQTHoyRU5F4+aTSVjO+I8wU=
|
||||||
|
</data>
|
||||||
|
</dict>
|
||||||
|
<key>CommonAssets/warning.png</key>
|
||||||
|
<dict>
|
||||||
|
<key>hash2</key>
|
||||||
|
<data>
|
||||||
|
S7OOo4xdlAEiEgfkEuic4ap4JZlhvtYIwFSHWu034SA=
|
||||||
|
</data>
|
||||||
|
</dict>
|
||||||
|
<key>EncryptedOtpelf.js</key>
|
||||||
|
<dict>
|
||||||
|
<key>hash2</key>
|
||||||
|
<data>
|
||||||
|
85Tocsg1aPekBFPeMKMV2IruNGVkOyyV6xlbQkQOH3A=
|
||||||
|
</data>
|
||||||
|
</dict>
|
||||||
|
<key>Hash.txt</key>
|
||||||
|
<dict>
|
||||||
|
<key>hash2</key>
|
||||||
|
<data>
|
||||||
|
B20CsAEe8H45a3Ivw23aBTWNU7qGh1JYKb/iAwwaIEQ=
|
||||||
|
</data>
|
||||||
|
</dict>
|
||||||
|
<key>PrivacyInfo.xcprivacy</key>
|
||||||
|
<dict>
|
||||||
|
<key>hash2</key>
|
||||||
|
<data>
|
||||||
|
0GK4q+J5XVD2O8agumlONknk2PSlkzr97y/P84XeCyg=
|
||||||
|
</data>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<key>rules</key>
|
||||||
|
<dict>
|
||||||
|
<key>^.*</key>
|
||||||
|
<true/>
|
||||||
|
<key>^.*\.lproj/</key>
|
||||||
|
<dict>
|
||||||
|
<key>optional</key>
|
||||||
|
<true/>
|
||||||
|
<key>weight</key>
|
||||||
|
<real>1000</real>
|
||||||
|
</dict>
|
||||||
|
<key>^.*\.lproj/locversion.plist$</key>
|
||||||
|
<dict>
|
||||||
|
<key>omit</key>
|
||||||
|
<true/>
|
||||||
|
<key>weight</key>
|
||||||
|
<real>1100</real>
|
||||||
|
</dict>
|
||||||
|
<key>^Base\.lproj/</key>
|
||||||
|
<dict>
|
||||||
|
<key>weight</key>
|
||||||
|
<real>1010</real>
|
||||||
|
</dict>
|
||||||
|
<key>^version.plist$</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
<key>rules2</key>
|
||||||
|
<dict>
|
||||||
|
<key>.*\.dSYM($|/)</key>
|
||||||
|
<dict>
|
||||||
|
<key>weight</key>
|
||||||
|
<real>11</real>
|
||||||
|
</dict>
|
||||||
|
<key>^(.*/)?\.DS_Store$</key>
|
||||||
|
<dict>
|
||||||
|
<key>omit</key>
|
||||||
|
<true/>
|
||||||
|
<key>weight</key>
|
||||||
|
<real>2000</real>
|
||||||
|
</dict>
|
||||||
|
<key>^.*</key>
|
||||||
|
<true/>
|
||||||
|
<key>^.*\.lproj/</key>
|
||||||
|
<dict>
|
||||||
|
<key>optional</key>
|
||||||
|
<true/>
|
||||||
|
<key>weight</key>
|
||||||
|
<real>1000</real>
|
||||||
|
</dict>
|
||||||
|
<key>^.*\.lproj/locversion.plist$</key>
|
||||||
|
<dict>
|
||||||
|
<key>omit</key>
|
||||||
|
<true/>
|
||||||
|
<key>weight</key>
|
||||||
|
<real>1100</real>
|
||||||
|
</dict>
|
||||||
|
<key>^Base\.lproj/</key>
|
||||||
|
<dict>
|
||||||
|
<key>weight</key>
|
||||||
|
<real>1010</real>
|
||||||
|
</dict>
|
||||||
|
<key>^Info\.plist$</key>
|
||||||
|
<dict>
|
||||||
|
<key>omit</key>
|
||||||
|
<true/>
|
||||||
|
<key>weight</key>
|
||||||
|
<real>20</real>
|
||||||
|
</dict>
|
||||||
|
<key>^PkgInfo$</key>
|
||||||
|
<dict>
|
||||||
|
<key>omit</key>
|
||||||
|
<true/>
|
||||||
|
<key>weight</key>
|
||||||
|
<real>20</real>
|
||||||
|
</dict>
|
||||||
|
<key>^embedded\.provisionprofile$</key>
|
||||||
|
<dict>
|
||||||
|
<key>weight</key>
|
||||||
|
<real>20</real>
|
||||||
|
</dict>
|
||||||
|
<key>^version\.plist$</key>
|
||||||
|
<dict>
|
||||||
|
<key>weight</key>
|
||||||
|
<real>20</real>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
30
test/Freshyo.app/Frameworks/hermes.framework/Info.plist
Normal file
30
test/Freshyo.app/Frameworks/hermes.framework/Info.plist
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>CFBundleDevelopmentRegion</key>
|
||||||
|
<string>English</string>
|
||||||
|
<key>CFBundleExecutable</key>
|
||||||
|
<string>hermes</string>
|
||||||
|
<key>CFBundleIconFile</key>
|
||||||
|
<string></string>
|
||||||
|
<key>CFBundleIdentifier</key>
|
||||||
|
<string>dev.hermesengine.iphonesimulator</string>
|
||||||
|
<key>CFBundleInfoDictionaryVersion</key>
|
||||||
|
<string>6.0</string>
|
||||||
|
<key>CFBundleName</key>
|
||||||
|
<string></string>
|
||||||
|
<key>CFBundlePackageType</key>
|
||||||
|
<string>FMWK</string>
|
||||||
|
<key>CFBundleShortVersionString</key>
|
||||||
|
<string>0.12.0</string>
|
||||||
|
<key>CFBundleSignature</key>
|
||||||
|
<string>????</string>
|
||||||
|
<key>CFBundleVersion</key>
|
||||||
|
<string>0.12.0</string>
|
||||||
|
<key>CSResourcesFileMapped</key>
|
||||||
|
<true/>
|
||||||
|
<key>MinimumOSVersion</key>
|
||||||
|
<string>15.1</string>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
|
|
@ -0,0 +1,101 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>files</key>
|
||||||
|
<dict>
|
||||||
|
<key>Info.plist</key>
|
||||||
|
<data>
|
||||||
|
4Hno0Ddszl7pNxmsMdj4eZ8APpg=
|
||||||
|
</data>
|
||||||
|
</dict>
|
||||||
|
<key>files2</key>
|
||||||
|
<dict/>
|
||||||
|
<key>rules</key>
|
||||||
|
<dict>
|
||||||
|
<key>^.*</key>
|
||||||
|
<true/>
|
||||||
|
<key>^.*\.lproj/</key>
|
||||||
|
<dict>
|
||||||
|
<key>optional</key>
|
||||||
|
<true/>
|
||||||
|
<key>weight</key>
|
||||||
|
<real>1000</real>
|
||||||
|
</dict>
|
||||||
|
<key>^.*\.lproj/locversion.plist$</key>
|
||||||
|
<dict>
|
||||||
|
<key>omit</key>
|
||||||
|
<true/>
|
||||||
|
<key>weight</key>
|
||||||
|
<real>1100</real>
|
||||||
|
</dict>
|
||||||
|
<key>^Base\.lproj/</key>
|
||||||
|
<dict>
|
||||||
|
<key>weight</key>
|
||||||
|
<real>1010</real>
|
||||||
|
</dict>
|
||||||
|
<key>^version.plist$</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
<key>rules2</key>
|
||||||
|
<dict>
|
||||||
|
<key>.*\.dSYM($|/)</key>
|
||||||
|
<dict>
|
||||||
|
<key>weight</key>
|
||||||
|
<real>11</real>
|
||||||
|
</dict>
|
||||||
|
<key>^(.*/)?\.DS_Store$</key>
|
||||||
|
<dict>
|
||||||
|
<key>omit</key>
|
||||||
|
<true/>
|
||||||
|
<key>weight</key>
|
||||||
|
<real>2000</real>
|
||||||
|
</dict>
|
||||||
|
<key>^.*</key>
|
||||||
|
<true/>
|
||||||
|
<key>^.*\.lproj/</key>
|
||||||
|
<dict>
|
||||||
|
<key>optional</key>
|
||||||
|
<true/>
|
||||||
|
<key>weight</key>
|
||||||
|
<real>1000</real>
|
||||||
|
</dict>
|
||||||
|
<key>^.*\.lproj/locversion.plist$</key>
|
||||||
|
<dict>
|
||||||
|
<key>omit</key>
|
||||||
|
<true/>
|
||||||
|
<key>weight</key>
|
||||||
|
<real>1100</real>
|
||||||
|
</dict>
|
||||||
|
<key>^Base\.lproj/</key>
|
||||||
|
<dict>
|
||||||
|
<key>weight</key>
|
||||||
|
<real>1010</real>
|
||||||
|
</dict>
|
||||||
|
<key>^Info\.plist$</key>
|
||||||
|
<dict>
|
||||||
|
<key>omit</key>
|
||||||
|
<true/>
|
||||||
|
<key>weight</key>
|
||||||
|
<real>20</real>
|
||||||
|
</dict>
|
||||||
|
<key>^PkgInfo$</key>
|
||||||
|
<dict>
|
||||||
|
<key>omit</key>
|
||||||
|
<true/>
|
||||||
|
<key>weight</key>
|
||||||
|
<real>20</real>
|
||||||
|
</dict>
|
||||||
|
<key>^embedded\.provisionprofile$</key>
|
||||||
|
<dict>
|
||||||
|
<key>weight</key>
|
||||||
|
<real>20</real>
|
||||||
|
</dict>
|
||||||
|
<key>^version\.plist$</key>
|
||||||
|
<dict>
|
||||||
|
<key>weight</key>
|
||||||
|
<real>20</real>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
BIN
test/Freshyo.app/Frameworks/hermes.framework/hermes
Executable file
BIN
test/Freshyo.app/Frameworks/hermes.framework/hermes
Executable file
Binary file not shown.
BIN
test/Freshyo.app/Freshyo
Executable file
BIN
test/Freshyo.app/Freshyo
Executable file
Binary file not shown.
BIN
test/Freshyo.app/Freshyo.debug.dylib
Executable file
BIN
test/Freshyo.app/Freshyo.debug.dylib
Executable file
Binary file not shown.
BIN
test/Freshyo.app/GTMAppAuth_Privacy.bundle/Info.plist
Normal file
BIN
test/Freshyo.app/GTMAppAuth_Privacy.bundle/Info.plist
Normal file
Binary file not shown.
|
|
@ -0,0 +1,14 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>NSPrivacyCollectedDataTypes</key>
|
||||||
|
<array/>
|
||||||
|
<key>NSPrivacyAccessedAPITypes</key>
|
||||||
|
<array/>
|
||||||
|
<key>NSPrivacyTrackingDomains</key>
|
||||||
|
<array/>
|
||||||
|
<key>NSPrivacyTracking</key>
|
||||||
|
<false/>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
Binary file not shown.
|
|
@ -0,0 +1,23 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>NSPrivacyAccessedAPITypes</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>NSPrivacyAccessedAPIType</key>
|
||||||
|
<string>NSPrivacyAccessedAPICategoryUserDefaults</string>
|
||||||
|
<key>NSPrivacyAccessedAPITypeReasons</key>
|
||||||
|
<array>
|
||||||
|
<string>CA92.1</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
|
<key>NSPrivacyCollectedDataTypes</key>
|
||||||
|
<array/>
|
||||||
|
<key>NSPrivacyTracking</key>
|
||||||
|
<false/>
|
||||||
|
<key>NSPrivacyTrackingDomains</key>
|
||||||
|
<array/>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
BIN
test/Freshyo.app/GoogleSignIn.bundle/Info.plist
Normal file
BIN
test/Freshyo.app/GoogleSignIn.bundle/Info.plist
Normal file
Binary file not shown.
122
test/Freshyo.app/GoogleSignIn.bundle/PrivacyInfo.xcprivacy
Normal file
122
test/Freshyo.app/GoogleSignIn.bundle/PrivacyInfo.xcprivacy
Normal file
|
|
@ -0,0 +1,122 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>NSPrivacyTracking</key>
|
||||||
|
<false/>
|
||||||
|
<key>NSPrivacyTrackingDomains</key>
|
||||||
|
<array/>
|
||||||
|
<key>NSPrivacyCollectedDataTypes</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>NSPrivacyCollectedDataType</key>
|
||||||
|
<string>NSPrivacyCollectedDataTypeName</string>
|
||||||
|
<key>NSPrivacyCollectedDataTypeLinked</key>
|
||||||
|
<true/>
|
||||||
|
<key>NSPrivacyCollectedDataTypeTracking</key>
|
||||||
|
<false/>
|
||||||
|
<key>NSPrivacyCollectedDataTypePurposes</key>
|
||||||
|
<array>
|
||||||
|
<string>NSPrivacyCollectedDataTypePurposeAppFunctionality</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>NSPrivacyCollectedDataType</key>
|
||||||
|
<string>NSPrivacyCollectedDataTypeEmailAddress</string>
|
||||||
|
<key>NSPrivacyCollectedDataTypeLinked</key>
|
||||||
|
<true/>
|
||||||
|
<key>NSPrivacyCollectedDataTypeTracking</key>
|
||||||
|
<false/>
|
||||||
|
<key>NSPrivacyCollectedDataTypePurposes</key>
|
||||||
|
<array>
|
||||||
|
<string>NSPrivacyCollectedDataTypePurposeAppFunctionality</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>NSPrivacyCollectedDataType</key>
|
||||||
|
<string>NSPrivacyCollectedDataTypePhoneNumber</string>
|
||||||
|
<key>NSPrivacyCollectedDataTypeLinked</key>
|
||||||
|
<true/>
|
||||||
|
<key>NSPrivacyCollectedDataTypeTracking</key>
|
||||||
|
<false/>
|
||||||
|
<key>NSPrivacyCollectedDataTypePurposes</key>
|
||||||
|
<array>
|
||||||
|
<string>NSPrivacyCollectedDataTypePurposeAppFunctionality</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>NSPrivacyCollectedDataType</key>
|
||||||
|
<string>NSPrivacyCollectedDataTypeOtherDataTypes</string>
|
||||||
|
<key>NSPrivacyCollectedDataTypeLinked</key>
|
||||||
|
<true/>
|
||||||
|
<key>NSPrivacyCollectedDataTypeTracking</key>
|
||||||
|
<false/>
|
||||||
|
<key>NSPrivacyCollectedDataTypePurposes</key>
|
||||||
|
<array>
|
||||||
|
<string>NSPrivacyCollectedDataTypePurposeAppFunctionality</string>
|
||||||
|
<string>NSPrivacyCollectedDataTypePurposeAnalytics</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>NSPrivacyCollectedDataType</key>
|
||||||
|
<string>NSPrivacyCollectedDataTypeCoarseLocation</string>
|
||||||
|
<key>NSPrivacyCollectedDataTypeLinked</key>
|
||||||
|
<true/>
|
||||||
|
<key>NSPrivacyCollectedDataTypeTracking</key>
|
||||||
|
<false/>
|
||||||
|
<key>NSPrivacyCollectedDataTypePurposes</key>
|
||||||
|
<array>
|
||||||
|
<string>NSPrivacyCollectedDataTypePurposeAppFunctionality</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>NSPrivacyCollectedDataType</key>
|
||||||
|
<string>NSPrivacyCollectedDataTypeUserID</string>
|
||||||
|
<key>NSPrivacyCollectedDataTypeLinked</key>
|
||||||
|
<true/>
|
||||||
|
<key>NSPrivacyCollectedDataTypeTracking</key>
|
||||||
|
<false/>
|
||||||
|
<key>NSPrivacyCollectedDataTypePurposes</key>
|
||||||
|
<array>
|
||||||
|
<string>NSPrivacyCollectedDataTypePurposeAppFunctionality</string>
|
||||||
|
<string>NSPrivacyCollectedDataTypePurposeAnalytics</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>NSPrivacyCollectedDataType</key>
|
||||||
|
<string>NSPrivacyCollectedDataTypeDeviceID</string>
|
||||||
|
<key>NSPrivacyCollectedDataTypeLinked</key>
|
||||||
|
<true/>
|
||||||
|
<key>NSPrivacyCollectedDataTypeTracking</key>
|
||||||
|
<false/>
|
||||||
|
<key>NSPrivacyCollectedDataTypePurposes</key>
|
||||||
|
<array>
|
||||||
|
<string>NSPrivacyCollectedDataTypePurposeAnalytics</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>NSPrivacyCollectedDataType</key>
|
||||||
|
<string>NSPrivacyCollectedDataTypeOtherUsageData</string>
|
||||||
|
<key>NSPrivacyCollectedDataTypeLinked</key>
|
||||||
|
<true/>
|
||||||
|
<key>NSPrivacyCollectedDataTypeTracking</key>
|
||||||
|
<false/>
|
||||||
|
<key>NSPrivacyCollectedDataTypePurposes</key>
|
||||||
|
<array>
|
||||||
|
<string>NSPrivacyCollectedDataTypePurposeAnalytics</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
|
<key>NSPrivacyAccessedAPITypes</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>NSPrivacyAccessedAPITypeReasons</key>
|
||||||
|
<array>
|
||||||
|
<string>CA92.1</string>
|
||||||
|
</array>
|
||||||
|
<key>NSPrivacyAccessedAPIType</key>
|
||||||
|
<string>NSPrivacyAccessedAPICategoryUserDefaults</string>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
BIN
test/Freshyo.app/GoogleSignIn.bundle/Roboto-Bold.ttf
Executable file
BIN
test/Freshyo.app/GoogleSignIn.bundle/Roboto-Bold.ttf
Executable file
Binary file not shown.
|
|
@ -0,0 +1,32 @@
|
||||||
|
/* Sign-in button text */
|
||||||
|
"Sign in" = "تسجيل الدخول";
|
||||||
|
|
||||||
|
/* Long form sign-in button text */
|
||||||
|
"Sign in with Google" = "تسجيل الدخول باستخدام Google";
|
||||||
|
|
||||||
|
/* The text for the button for user to acknowledge and dismiss a dialog. */
|
||||||
|
"OK" = "موافق";
|
||||||
|
|
||||||
|
/* The text for the button for user to dismiss a dialog without taking any action. */
|
||||||
|
"Cancel" = "إلغاء";
|
||||||
|
|
||||||
|
/* The name of the iOS native "Settings" app. */
|
||||||
|
"SettingsAppName" = "إعدادات";
|
||||||
|
|
||||||
|
/* The title for the error dialog for unable to sign in because of EMM policy. */
|
||||||
|
"EmmErrorTitle" = "يتعذَّر تسجيل الدخول إلى الحساب";
|
||||||
|
|
||||||
|
/* The text in the error dialog asking user to set up a passcode for the device due to EMM policy. */
|
||||||
|
"EmmPasscodeRequired" = "يطلب منك المشرف تعيين رمز مرور على هذا الجهاز للدخول إلى هذا الحساب. يُرجى تعيين رمز المرور وإعادة المحاولة.";
|
||||||
|
|
||||||
|
/* The text in the error dialog informing user that EMM policy prevented sign-in on the device. */
|
||||||
|
"EmmGeneralError" = "لا يتوافق هذا الجهاز مع سياسة الأمان التي أعدها مشرفك";
|
||||||
|
|
||||||
|
/* The title in the error dialog informing user that connecting with Device Policy app is required. */
|
||||||
|
"EmmConnectTitle" = "هل تريد الربط بتطبيق Device Policy؟";
|
||||||
|
|
||||||
|
/* The text in the error dialog informing user that connecting with Device Policy app is required. */
|
||||||
|
"EmmConnectText" = "يجب الربط مع تطبيق Device Policy قبل تسجيل الدخول لحماية بيانات مؤسستك.";
|
||||||
|
|
||||||
|
/* The action button label in the error dialog informing user that connecting with Device Policy app is required. */
|
||||||
|
"EmmConnectLabel" = "ربط";
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
/* Sign-in button text */
|
||||||
|
"Sign in" = "Inicia la sessió";
|
||||||
|
|
||||||
|
/* Long form sign-in button text */
|
||||||
|
"Sign in with Google" = "Inicia la sessió amb Google";
|
||||||
|
|
||||||
|
/* The text for the button for user to acknowledge and dismiss a dialog. */
|
||||||
|
"OK" = "D’acord";
|
||||||
|
|
||||||
|
/* The text for the button for user to dismiss a dialog without taking any action. */
|
||||||
|
"Cancel" = "Cancel·la";
|
||||||
|
|
||||||
|
/* The name of the iOS native "Settings" app. */
|
||||||
|
"SettingsAppName" = "Configuració";
|
||||||
|
|
||||||
|
/* The title for the error dialog for unable to sign in because of EMM policy. */
|
||||||
|
"EmmErrorTitle" = "No es pot iniciar la sessió al compte";
|
||||||
|
|
||||||
|
/* The text in the error dialog asking user to set up a passcode for the device due to EMM policy. */
|
||||||
|
"EmmPasscodeRequired" = "L'administrador requereix que estableixis una contrasenya en aquest dispositiu per accedir al compte. Estableix una contrasenya i torna-ho a provar.";
|
||||||
|
|
||||||
|
/* The text in the error dialog informing user that EMM policy prevented sign-in on the device. */
|
||||||
|
"EmmGeneralError" = "El dispositiu no compleix la política de seguretat establerta pel teu administrador.";
|
||||||
|
|
||||||
|
/* The title in the error dialog informing user that connecting with Device Policy app is required. */
|
||||||
|
"EmmConnectTitle" = "Vols connectar-te amb l'aplicació Device Policy?";
|
||||||
|
|
||||||
|
/* The text in the error dialog informing user that connecting with Device Policy app is required. */
|
||||||
|
"EmmConnectText" = "Per protegir les dades de la teva organització, t'has de connectar amb l'aplicació Device Policy abans d'iniciar la sessió.";
|
||||||
|
|
||||||
|
/* The action button label in the error dialog informing user that connecting with Device Policy app is required. */
|
||||||
|
"EmmConnectLabel" = "Vull connectar-me";
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
/* Sign-in button text */
|
||||||
|
"Sign in" = "Přihlásit se";
|
||||||
|
|
||||||
|
/* Long form sign-in button text */
|
||||||
|
"Sign in with Google" = "Přihlásit se účtem Google";
|
||||||
|
|
||||||
|
/* The text for the button for user to acknowledge and dismiss a dialog. */
|
||||||
|
"OK" = "OK";
|
||||||
|
|
||||||
|
/* The text for the button for user to dismiss a dialog without taking any action. */
|
||||||
|
"Cancel" = "Zrušit";
|
||||||
|
|
||||||
|
/* The name of the iOS native "Settings" app. */
|
||||||
|
"SettingsAppName" = "Nastavení";
|
||||||
|
|
||||||
|
/* The title for the error dialog for unable to sign in because of EMM policy. */
|
||||||
|
"EmmErrorTitle" = "Nelze se přihlásit k účtu";
|
||||||
|
|
||||||
|
/* The text in the error dialog asking user to set up a passcode for the device due to EMM policy. */
|
||||||
|
"EmmPasscodeRequired" = "Administrátor vyžaduje, abyste v tomto zařízení nastavili heslo pro přístup k tomuto účtu. Nastavte prosím heslo a zkuste to znovu.";
|
||||||
|
|
||||||
|
/* The text in the error dialog informing user that EMM policy prevented sign-in on the device. */
|
||||||
|
"EmmGeneralError" = "Zařízení nevyhovuje bezpečnostním zásadám nastaveným administrátorem.";
|
||||||
|
|
||||||
|
/* The title in the error dialog informing user that connecting with Device Policy app is required. */
|
||||||
|
"EmmConnectTitle" = "Propojit s aplikací Device Policy?";
|
||||||
|
|
||||||
|
/* The text in the error dialog informing user that connecting with Device Policy app is required. */
|
||||||
|
"EmmConnectText" = "Aby bylo možné chránit data vaší organizace, před přihlášením je nutné aktivovat propojení s aplikací Device Policy.";
|
||||||
|
|
||||||
|
/* The action button label in the error dialog informing user that connecting with Device Policy app is required. */
|
||||||
|
"EmmConnectLabel" = "Propojit";
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
/* Sign-in button text */
|
||||||
|
"Sign in" = "Log ind";
|
||||||
|
|
||||||
|
/* Long form sign-in button text */
|
||||||
|
"Sign in with Google" = "Log ind med Google";
|
||||||
|
|
||||||
|
/* The text for the button for user to acknowledge and dismiss a dialog. */
|
||||||
|
"OK" = "OK";
|
||||||
|
|
||||||
|
/* The text for the button for user to dismiss a dialog without taking any action. */
|
||||||
|
"Cancel" = "Annuller";
|
||||||
|
|
||||||
|
/* The name of the iOS native "Settings" app. */
|
||||||
|
"SettingsAppName" = "Indstillinger";
|
||||||
|
|
||||||
|
/* The title for the error dialog for unable to sign in because of EMM policy. */
|
||||||
|
"EmmErrorTitle" = "Der kunne ikke logges ind på kontoen";
|
||||||
|
|
||||||
|
/* The text in the error dialog asking user to set up a passcode for the device due to EMM policy. */
|
||||||
|
"EmmPasscodeRequired" = "Din administrator kræver, at du angiver en adgangskode på enheden for at få adgang til kontoen. Angiv en adgangskode, og prøv igen.";
|
||||||
|
|
||||||
|
/* The text in the error dialog informing user that EMM policy prevented sign-in on the device. */
|
||||||
|
"EmmGeneralError" = "Enheden overholder ikke den sikkerhedspolitik, der er angivet af din administrator.";
|
||||||
|
|
||||||
|
/* The title in the error dialog informing user that connecting with Device Policy app is required. */
|
||||||
|
"EmmConnectTitle" = "Vil du oprette forbindelse til appen Device Policy?";
|
||||||
|
|
||||||
|
/* The text in the error dialog informing user that connecting with Device Policy app is required. */
|
||||||
|
"EmmConnectText" = "Du skal oprette forbindelse til appen Device Policy, inden du logger ind, for at beskytte din organisations data.";
|
||||||
|
|
||||||
|
/* The action button label in the error dialog informing user that connecting with Device Policy app is required. */
|
||||||
|
"EmmConnectLabel" = "Opret forbindelse";
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
/* Sign-in button text */
|
||||||
|
"Sign in" = "Anmelden";
|
||||||
|
|
||||||
|
/* Long form sign-in button text */
|
||||||
|
"Sign in with Google" = "Über Google anmelden";
|
||||||
|
|
||||||
|
/* The text for the button for user to acknowledge and dismiss a dialog. */
|
||||||
|
"OK" = "OK";
|
||||||
|
|
||||||
|
/* The text for the button for user to dismiss a dialog without taking any action. */
|
||||||
|
"Cancel" = "Abbrechen";
|
||||||
|
|
||||||
|
/* The name of the iOS native "Settings" app. */
|
||||||
|
"SettingsAppName" = "Einstellungen";
|
||||||
|
|
||||||
|
/* The title for the error dialog for unable to sign in because of EMM policy. */
|
||||||
|
"EmmErrorTitle" = "Anmelden im Konto nicht möglich";
|
||||||
|
|
||||||
|
/* The text in the error dialog asking user to set up a passcode for the device due to EMM policy. */
|
||||||
|
"EmmPasscodeRequired" = "Ihr Administrator hat festgelegt, dass auf diesem Gerät ein Sicherheitscode eingerichtet werden muss, um auf dieses Konto zuzugreifen. Bitte legen Sie einen Sicherheitscode fest und versuchen Sie es noch einmal.";
|
||||||
|
|
||||||
|
/* The text in the error dialog informing user that EMM policy prevented sign-in on the device. */
|
||||||
|
"EmmGeneralError" = "Das Gerät ist nicht mit den von Ihrem Administrator festgelegten Sicherheitsrichtlinien konform.";
|
||||||
|
|
||||||
|
/* The title in the error dialog informing user that connecting with Device Policy app is required. */
|
||||||
|
"EmmConnectTitle" = "Mit der Device Policy App verknüpfen?";
|
||||||
|
|
||||||
|
/* The text in the error dialog informing user that connecting with Device Policy app is required. */
|
||||||
|
"EmmConnectText" = "Zum Schutz der Daten Ihrer Organisation müssen Sie Ihr Gerät zuerst mit der Device Policy App verknüpfen, bevor Sie sich anmelden.";
|
||||||
|
|
||||||
|
/* The action button label in the error dialog informing user that connecting with Device Policy app is required. */
|
||||||
|
"EmmConnectLabel" = "Verknüpfen";
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
/* Sign-in button text */
|
||||||
|
"Sign in" = "Σύνδεση";
|
||||||
|
|
||||||
|
/* Long form sign-in button text */
|
||||||
|
"Sign in with Google" = "Συνδεθείτε με το Google";
|
||||||
|
|
||||||
|
/* The text for the button for user to acknowledge and dismiss a dialog. */
|
||||||
|
"OK" = "ΟΚ";
|
||||||
|
|
||||||
|
/* The text for the button for user to dismiss a dialog without taking any action. */
|
||||||
|
"Cancel" = "Άκυρο";
|
||||||
|
|
||||||
|
/* The name of the iOS native "Settings" app. */
|
||||||
|
"SettingsAppName" = "Ρυθμίσεις";
|
||||||
|
|
||||||
|
/* The title for the error dialog for unable to sign in because of EMM policy. */
|
||||||
|
"EmmErrorTitle" = "Δεν είναι δυνατή η σύνδεση στον λογαριασμό";
|
||||||
|
|
||||||
|
/* The text in the error dialog asking user to set up a passcode for the device due to EMM policy. */
|
||||||
|
"EmmPasscodeRequired" = "Ο διαχειριστής σας απαιτεί να ορίσετε έναν κωδικό πρόσβασης στη συσκευή, για να έχετε πρόσβαση σε αυτόν τον λογαριασμό. Ορίστε έναν κωδικό πρόσβασης και δοκιμάστε ξανά.";
|
||||||
|
|
||||||
|
/* The text in the error dialog informing user that EMM policy prevented sign-in on the device. */
|
||||||
|
"EmmGeneralError" = "Η συσκευή δεν συμμορφώνεται με την πολιτική ασφαλείας που έχει ορίσει ο διαχειριστής σας.";
|
||||||
|
|
||||||
|
/* The title in the error dialog informing user that connecting with Device Policy app is required. */
|
||||||
|
"EmmConnectTitle" = "Σύνδεση με την εφαρμογή Device Policy;";
|
||||||
|
|
||||||
|
/* The text in the error dialog informing user that connecting with Device Policy app is required. */
|
||||||
|
"EmmConnectText" = "Προκειμένου να προστατεύσετε τα δεδομένα του οργανισμού σας, θα πρέπει να συνδεθείτε με την εφαρμογή Device Policy προτού συνδεθείτε στον λογαριασμό σας.";
|
||||||
|
|
||||||
|
/* The action button label in the error dialog informing user that connecting with Device Policy app is required. */
|
||||||
|
"EmmConnectLabel" = "Σύνδεση";
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
/* Sign-in button text */
|
||||||
|
"Sign in" = "Sign in";
|
||||||
|
|
||||||
|
/* Long form sign-in button text */
|
||||||
|
"Sign in with Google" = "Sign in with Google";
|
||||||
|
|
||||||
|
/* The text for the button for user to acknowledge and dismiss a dialog. */
|
||||||
|
"OK" = "OK";
|
||||||
|
|
||||||
|
/* The text for the button for user to dismiss a dialog without taking any action. */
|
||||||
|
"Cancel" = "Cancel";
|
||||||
|
|
||||||
|
/* The name of the iOS native "Settings" app. */
|
||||||
|
"SettingsAppName" = "Settings";
|
||||||
|
|
||||||
|
/* The title for the error dialog for unable to sign in because of EMM policy. */
|
||||||
|
"EmmErrorTitle" = "Unable to sign in to account";
|
||||||
|
|
||||||
|
/* The text in the error dialog asking user to set up a passcode for the device due to EMM policy. */
|
||||||
|
"EmmPasscodeRequired" = "Your administrator requires you to set a passcode on this device to access this account. Please set a passcode and try again.";
|
||||||
|
|
||||||
|
/* The text in the error dialog informing user that EMM policy prevented sign-in on the device. */
|
||||||
|
"EmmGeneralError" = "The device is not compliant with the security policy set by your administrator.";
|
||||||
|
|
||||||
|
/* The title in the error dialog informing user that connecting with Device Policy app is required. */
|
||||||
|
"EmmConnectTitle" = "Connect with Device Policy App?";
|
||||||
|
|
||||||
|
/* The text in the error dialog informing user that connecting with Device Policy app is required. */
|
||||||
|
"EmmConnectText" = "In order to protect your organization's data, you must connect with the Device Policy app before logging in.";
|
||||||
|
|
||||||
|
/* The action button label in the error dialog informing user that connecting with Device Policy app is required. */
|
||||||
|
"EmmConnectLabel" = "Connect";
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
/* Sign-in button text */
|
||||||
|
"Sign in" = "Sign in";
|
||||||
|
|
||||||
|
/* Long form sign-in button text */
|
||||||
|
"Sign in with Google" = "Sign in with Google";
|
||||||
|
|
||||||
|
/* The text for the button for user to acknowledge and dismiss a dialog. */
|
||||||
|
"OK" = "OK";
|
||||||
|
|
||||||
|
/* The text for the button for user to dismiss a dialog without taking any action. */
|
||||||
|
"Cancel" = "Cancel";
|
||||||
|
|
||||||
|
/* The name of the iOS native "Settings" app. */
|
||||||
|
"SettingsAppName" = "Settings";
|
||||||
|
|
||||||
|
/* The title for the error dialog for unable to sign in because of EMM policy. */
|
||||||
|
"EmmErrorTitle" = "Unable to sign in to account";
|
||||||
|
|
||||||
|
/* The text in the error dialog asking user to set up a passcode for the device due to EMM policy. */
|
||||||
|
"EmmPasscodeRequired" = "Your administrator requires you to set a passcode on this device to access this account. Please set a passcode and try again.";
|
||||||
|
|
||||||
|
/* The text in the error dialog informing user that EMM policy prevented sign-in on the device. */
|
||||||
|
"EmmGeneralError" = "The device is not compliant with the security policy set by your administrator.";
|
||||||
|
|
||||||
|
/* The title in the error dialog informing user that connecting with Device Policy app is required. */
|
||||||
|
"EmmConnectTitle" = "Connect with Device Policy App?";
|
||||||
|
|
||||||
|
/* The text in the error dialog informing user that connecting with Device Policy app is required. */
|
||||||
|
"EmmConnectText" = "In order to protect your organisation's data, you must connect with the Device Policy app before logging in.";
|
||||||
|
|
||||||
|
/* The action button label in the error dialog informing user that connecting with Device Policy app is required. */
|
||||||
|
"EmmConnectLabel" = "Connect";
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
/* Sign-in button text */
|
||||||
|
"Sign in" = "Iniciar sesión";
|
||||||
|
|
||||||
|
/* Long form sign-in button text */
|
||||||
|
"Sign in with Google" = "Iniciar sesión con Google";
|
||||||
|
|
||||||
|
/* The text for the button for user to acknowledge and dismiss a dialog. */
|
||||||
|
"OK" = "Aceptar";
|
||||||
|
|
||||||
|
/* The text for the button for user to dismiss a dialog without taking any action. */
|
||||||
|
"Cancel" = "Cancelar";
|
||||||
|
|
||||||
|
/* The name of the iOS native "Settings" app. */
|
||||||
|
"SettingsAppName" = "Configuración";
|
||||||
|
|
||||||
|
/* The title for the error dialog for unable to sign in because of EMM policy. */
|
||||||
|
"EmmErrorTitle" = "No se ha podido iniciar sesión en la cuenta";
|
||||||
|
|
||||||
|
/* The text in the error dialog asking user to set up a passcode for the device due to EMM policy. */
|
||||||
|
"EmmPasscodeRequired" = "El administrador requiere que configures una contraseña en este dispositivo para acceder a esta cuenta. Inténtalo de nuevo cuando lo hayas hecho.";
|
||||||
|
|
||||||
|
/* The text in the error dialog informing user that EMM policy prevented sign-in on the device. */
|
||||||
|
"EmmGeneralError" = "El dispositivo no cumple la política de privacidad que ha definido tu administrador.";
|
||||||
|
|
||||||
|
/* The title in the error dialog informing user that connecting with Device Policy app is required. */
|
||||||
|
"EmmConnectTitle" = "¿Has conectado tu dispositivo con la aplicación Device Policy?";
|
||||||
|
|
||||||
|
/* The text in the error dialog informing user that connecting with Device Policy app is required. */
|
||||||
|
"EmmConnectText" = "Para proteger los datos de tu organización, debes conectar tu dispositivo con la aplicación Device Policy antes de iniciar sesión.";
|
||||||
|
|
||||||
|
/* The action button label in the error dialog informing user that connecting with Device Policy app is required. */
|
||||||
|
"EmmConnectLabel" = "Conectar";
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
/* Sign-in button text */
|
||||||
|
"Sign in" = "Acceder";
|
||||||
|
|
||||||
|
/* Long form sign-in button text */
|
||||||
|
"Sign in with Google" = "Acceder con Google";
|
||||||
|
|
||||||
|
/* The text for the button for user to acknowledge and dismiss a dialog. */
|
||||||
|
"OK" = "Aceptar";
|
||||||
|
|
||||||
|
/* The text for the button for user to dismiss a dialog without taking any action. */
|
||||||
|
"Cancel" = "Cancelar";
|
||||||
|
|
||||||
|
/* The name of the iOS native "Settings" app. */
|
||||||
|
"SettingsAppName" = "Configuración";
|
||||||
|
|
||||||
|
/* The title for the error dialog for unable to sign in because of EMM policy. */
|
||||||
|
"EmmErrorTitle" = "No es posible acceder a la cuenta";
|
||||||
|
|
||||||
|
/* The text in the error dialog asking user to set up a passcode for the device due to EMM policy. */
|
||||||
|
"EmmPasscodeRequired" = "Para acceder a esta cuenta, tu administrador requiere que establezcas una contraseña en el dispositivo. Configúrala y vuelve a intentarlo.";
|
||||||
|
|
||||||
|
/* The text in the error dialog informing user that EMM policy prevented sign-in on the device. */
|
||||||
|
"EmmGeneralError" = "El dispositivo no cumple con la política de seguridad que estableció el administrador.";
|
||||||
|
|
||||||
|
/* The title in the error dialog informing user that connecting with Device Policy app is required. */
|
||||||
|
"EmmConnectTitle" = "¿Deseas conectarte con la app de Device Policy?";
|
||||||
|
|
||||||
|
/* The text in the error dialog informing user that connecting with Device Policy app is required. */
|
||||||
|
"EmmConnectText" = "Para proteger los datos de tu organización, debes conectarte con la app de Device Policy antes de acceder.";
|
||||||
|
|
||||||
|
/* The action button label in the error dialog informing user that connecting with Device Policy app is required. */
|
||||||
|
"EmmConnectLabel" = "Conectar";
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue