Compare commits

...
Sign in to create a new pull request.

5 commits

Author SHA1 Message Date
shafi54
84f1ea179f notice 2026-04-10 21:49:41 +05:30
shafi54
b38ff13950 enh 2026-03-20 14:48:31 +05:30
shafi54
e2abc7cb02 enh 2026-03-20 00:41:36 +05:30
shafi54
4f1f52ffee enh 2026-03-20 00:40:31 +05:30
shafi54
71cad727fd enh 2026-03-20 00:39:48 +05:30
209 changed files with 11558 additions and 3147 deletions

View file

@ -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
View file

@ -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

File diff suppressed because one or more lines are too long

View file

@ -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',

View 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>
</>
);
}

View 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;

View file

@ -5,8 +5,8 @@
}, },
"build": { "build": {
"development": { "development": {
"developmentClient": true, "distribution": "internal",
"distribution": "internal" "autoIncrement": true
}, },
"preview": { "preview": {
"distribution": "internal", "distribution": "internal",

View 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;

File diff suppressed because it is too large Load diff

View file

@ -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
} }
] ]
} }

View file

@ -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, ({}) => ({
}));

View file

@ -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');
};

View file

@ -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(),

View 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
};
}

View file

@ -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
/** /**

View file

@ -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;

View file

@ -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" };
}),
});

View file

@ -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({

View file

@ -5,8 +5,8 @@
}, },
"build": { "build": {
"development": { "development": {
"developmentClient": true,
"distribution": "internal", "distribution": "internal",
"autoIncrement": true,
"channel": "development" "channel": "development"
}, },
"preview": { "preview": {

View file

@ -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
View 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
View 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

Binary file not shown.

View file

@ -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>

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

BIN
test/Freshyo.app/Assets.car Normal file

Binary file not shown.

Binary file not shown.

View 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"}}

Binary file not shown.

BIN
test/Freshyo.app/Expo.plist Normal file

Binary file not shown.

View file

@ -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>

View file

@ -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>

Binary file not shown.

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

Binary file not shown.

View file

@ -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>

Binary file not shown.

View file

@ -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>

View file

@ -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.

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

View file

@ -0,0 +1 @@
12bd975b82f23eb997a3ee2df03e4e33

View file

@ -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>

View file

@ -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>

View 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>

View file

@ -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>

Binary file not shown.

BIN
test/Freshyo.app/Freshyo Executable file

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -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>

View file

@ -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>

Binary file not shown.

View 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>

Binary file not shown.

View file

@ -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" = "ربط";

View file

@ -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" = "Dacord";
/* 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";

View file

@ -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";

View file

@ -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";

View file

@ -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";

View file

@ -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" = "Σύνδεση";

View file

@ -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";

View file

@ -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";

View file

@ -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";

View file

@ -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