Initial commit: code as received (Create/Anything export)

Insole-production time tracker exported from the Create/Anything AI
platform. Baseline snapshot before any reverse-engineering or cleanup.

- apps/mobile: Expo Router app (iOS/Android/web), the only workspace
- publisher/: standalone OpenNext/AWS deploy tooling for the web side
- Backend (/api/tasks, /api/logs + DB) lives remotely, not in this repo
This commit is contained in:
Bas van Rossem
2026-06-17 10:19:33 +02:00
commit d94d0b188b
192 changed files with 50705 additions and 0 deletions

View File

@@ -0,0 +1,299 @@
import type { ExistingContact, ContactQuery } from 'expo-contacts';
import { Fields, SortTypes } from 'expo-contacts/src/Contacts';
import Alert from './alerts.web';
import * as Notifications from 'expo-contacts';
const { PermissionStatus } = Notifications;
export { PermissionStatus, Fields, SortTypes };
const fakeContacts: ExistingContact[] = [
{
id: '1',
contactType: 'person',
name: 'John Doe',
firstName: 'John',
lastName: 'Doe',
phoneNumbers: [
{ number: '+1 (555) 123-4567', isPrimary: true, label: 'mobile' },
{ number: '+1 (555) 987-6543', isPrimary: false, label: 'home' },
],
emails: [
{ email: 'john.doe@example.com', isPrimary: true, label: 'work' },
{ email: 'john.personal@gmail.com', isPrimary: false, label: 'personal' },
],
addresses: [
{
street: '123 Main St',
city: 'New York',
region: 'NY',
postalCode: '10001',
country: 'USA',
label: 'home',
},
],
birthday: { day: 15, month: 5, year: 1990 },
note: 'Met at conference',
},
{
id: '2',
contactType: 'person',
name: 'Jane Smith',
firstName: 'Jane',
lastName: 'Smith',
phoneNumbers: [{ number: '+1 (555) 234-5678', isPrimary: true, label: 'mobile' }],
emails: [{ email: 'jane.smith@company.com', isPrimary: true, label: 'work' }],
addresses: [
{
street: '456 Oak Ave',
city: 'Los Angeles',
region: 'CA',
postalCode: '90210',
country: 'USA',
label: 'home',
},
],
birthday: { day: 3, month: 12, year: 1985 },
note: 'College friend',
},
{
id: '3',
contactType: 'person',
name: 'Bob Johnson',
firstName: 'Bob',
lastName: 'Johnson',
phoneNumbers: [{ number: '+1 (555) 345-6789', isPrimary: true, label: 'mobile' }],
emails: [{ email: 'bob.johnson@email.com', isPrimary: true, label: 'personal' }],
addresses: [],
birthday: { day: 22, month: 8, year: 1992 },
note: 'Neighbor',
},
{
id: '4',
contactType: 'person',
name: 'Alice Williams',
firstName: 'Alice',
lastName: 'Williams',
phoneNumbers: [
{ number: '+1 (555) 456-7890', isPrimary: true, label: 'mobile' },
{ number: '+1 (555) 111-2222', isPrimary: false, label: 'work' },
],
emails: [{ email: 'alice.williams@startup.com', isPrimary: true, label: 'work' }],
addresses: [
{
street: '789 Pine St',
city: 'San Francisco',
region: 'CA',
postalCode: '94102',
country: 'USA',
label: 'work',
},
],
birthday: { day: 10, month: 3, year: 1988 },
note: 'Business partner',
},
{
id: '5',
contactType: 'person',
name: 'Charlie Brown',
firstName: 'Charlie',
lastName: 'Brown',
phoneNumbers: [{ number: '+1 (555) 567-8901', isPrimary: true, label: 'mobile' }],
emails: [{ email: 'charlie.brown@gmail.com', isPrimary: true, label: 'personal' }],
addresses: [],
birthday: { day: 18, month: 11, year: 1995 },
note: 'Gym buddy',
},
];
let permissionStatus = {
status: PermissionStatus.UNDETERMINED,
expires: 'never',
granted: false,
canAskAgain: true,
};
// since we polyfill fake contacts, we always return true
export const isAvailableAsync = async () => {
return true;
};
export const requestPermissionsAsync = async () => {
if (permissionStatus.status === PermissionStatus.GRANTED) {
return permissionStatus;
}
return new Promise((resolve) => {
Alert.alert(
'"Expo Go" Would Like to Access Your Contacts',
'Allow Expo projects to access your contacts',
[
{
text: "Don't Allow",
onPress: () => {
permissionStatus = {
status: PermissionStatus.DENIED,
expires: 'never',
granted: false,
canAskAgain: true,
};
resolve(permissionStatus);
},
style: 'default',
},
{
text: 'Continue',
onPress: () => {
permissionStatus = {
status: PermissionStatus.GRANTED,
expires: 'never',
granted: true,
canAskAgain: false,
};
resolve(permissionStatus);
},
style: 'default',
},
]
);
});
};
export const getPermissionsAsync = async () => {
return permissionStatus;
};
export const getContactsAsync = async (options: ContactQuery = {}) => {
const { sort = SortTypes.FirstName, pageSize, pageOffset } = options;
let contacts = [...fakeContacts];
if (sort === SortTypes.FirstName) {
contacts.sort((a, b) => (a.firstName || '').localeCompare(b.firstName || ''));
} else if (sort === SortTypes.LastName) {
contacts.sort((a, b) => (a.lastName || '').localeCompare(b.lastName || ''));
}
if (pageSize && pageOffset !== undefined) {
const startIndex = pageOffset * pageSize;
contacts = contacts.slice(startIndex, startIndex + pageSize);
}
return {
data: contacts,
hasNextPage: false,
hasPreviousPage: false,
total: fakeContacts.length,
};
};
export const getContactByIdAsync = async (id: string, _options: ContactQuery = {}) => {
const contact = fakeContacts.find((c) => c.id === id);
if (!contact) {
throw new Error(`Contact with id ${id} not found`);
}
return contact;
};
export const addContactAsync = async (contact: ExistingContact) => {
const newContact: ExistingContact = {
id: Date.now().toString(),
contactType: contact.contactType || 'person',
name: contact.name || '',
firstName: contact.firstName || '',
lastName: contact.lastName || '',
phoneNumbers: contact.phoneNumbers || [],
emails: contact.emails || [],
addresses: contact.addresses || [],
birthday: contact.birthday,
note: contact.note || '',
middleName: contact.middleName,
maidenName: contact.maidenName,
namePrefix: contact.namePrefix,
nameSuffix: contact.nameSuffix,
nickname: contact.nickname,
phoneticFirstName: contact.phoneticFirstName,
phoneticMiddleName: contact.phoneticMiddleName,
phoneticLastName: contact.phoneticLastName,
company: contact.company,
jobTitle: contact.jobTitle,
department: contact.department,
imageAvailable: contact.imageAvailable,
image: contact.image,
rawImage: contact.rawImage,
dates: contact.dates,
relationships: contact.relationships,
instantMessageAddresses: contact.instantMessageAddresses,
urlAddresses: contact.urlAddresses,
nonGregorianBirthday: contact.nonGregorianBirthday,
socialProfiles: contact.socialProfiles,
isFavorite: contact.isFavorite,
};
fakeContacts.push(newContact);
Alert.alert('Success', 'Contact added successfully!');
return newContact.id;
};
export const updateContactAsync = async (contact: ExistingContact) => {
const index = fakeContacts.findIndex((c) => c.id === contact.id);
if (index === -1) {
throw new Error(`Contact with id ${contact.id} not found`);
}
fakeContacts[index] = { ...fakeContacts[index], ...contact };
return contact.id;
};
export const removeContactAsync = async (contactId: string) => {
const index = fakeContacts.findIndex((c) => c.id === contactId);
if (index === -1) {
throw new Error(`Contact with id ${contactId} not found`);
}
fakeContacts.splice(index, 1);
setTimeout(() => {
Alert.alert('Success', 'Contact deleted successfully!');
}, 500);
return contactId;
};
const _createNoOpAsync = async () => {
Alert.alert('Not supported in the builder', 'Please use the Expo Go app to test this feature');
return { type: 'custom', data: null };
};
export const presentContactPickerAsync = async () => {
return _createNoOpAsync();
};
export const getGroupsAsync = async () => {
return _createNoOpAsync();
};
export const createGroupAsync = async () => {
return _createNoOpAsync();
};
export const removeGroupAsync = async () => {
return _createNoOpAsync();
};
export const updateGroupNameAsync = async () => {
return _createNoOpAsync();
};
export default {
Fields,
SortTypes,
PermissionStatus,
isAvailableAsync,
requestPermissionsAsync,
getPermissionsAsync,
getContactsAsync,
getContactByIdAsync,
addContactAsync,
updateContactAsync,
removeContactAsync,
presentContactPickerAsync,
getGroupsAsync,
createGroupAsync,
removeGroupAsync,
updateGroupNameAsync,
};