React Native Expo app — full mobile client

- Expo SDK 54, expo-router, NativeWind
- 7 screens: login, tasks, calendar, goals, chat, settings, tabs
- API client (api.hasdo.info), SecureStore auth, AuthContext
- Tab navigation: Ukoly, Kalendar, Cile, Chat, Nastaveni
- Pull-to-refresh, FAB, group colors, priority dots
- Calendar grid with task dots
- AI chat with keyboard avoiding
- Web export verified (780 modules, 1.5MB bundle)
- Bundle ID: info.hasdo.taskteam

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-29 15:09:35 +00:00
parent 545cf245f0
commit db81100b5b
18 changed files with 2796 additions and 58 deletions

64
mobile/lib/api.ts Normal file
View File

@@ -0,0 +1,64 @@
const API_BASE = 'https://api.hasdo.info';
export async function apiFetch<T>(
path: string,
opts: { method?: string; body?: any; token?: string } = {}
): Promise<T> {
const headers: Record<string, string> = { 'Content-Type': 'application/json' };
if (opts.token) headers['Authorization'] = `Bearer ${opts.token}`;
const res = await fetch(`${API_BASE}${path}`, {
method: opts.method || 'GET',
headers,
body: opts.body ? JSON.stringify(opts.body) : undefined,
});
if (!res.ok) {
const err = await res.json().catch(() => ({ message: `HTTP ${res.status}` }));
throw new Error(err.message || `HTTP ${res.status}`);
}
return res.json();
}
// Auth
export const login = (data: { email: string; password: string }) =>
apiFetch<{ data: { token: string; user: any } }>('/api/v1/auth/login', {
method: 'POST',
body: data,
});
export const register = (data: { email: string; name: string; password: string }) =>
apiFetch<{ data: { token: string; user: any } }>('/api/v1/auth/register', {
method: 'POST',
body: data,
});
// Tasks
export const getTasks = (token: string) =>
apiFetch<{ data: any[] }>('/api/v1/tasks', { token });
export const createTask = (token: string, data: any) =>
apiFetch<{ data: any }>('/api/v1/tasks', { method: 'POST', body: data, token });
export const updateTask = (token: string, id: string, data: any) =>
apiFetch<{ data: any }>(`/api/v1/tasks/${id}`, { method: 'PUT', body: data, token });
export const deleteTask = (token: string, id: string) =>
apiFetch<void>(`/api/v1/tasks/${id}`, { method: 'DELETE', token });
// Groups
export const getGroups = (token: string) =>
apiFetch<{ data: any[] }>('/api/v1/groups', { token });
// Goals
export const getGoals = (token: string) =>
apiFetch<{ data: any[] }>('/api/v1/goals', { token });
// Chat
export const sendChatMessage = (token: string, message: string) =>
apiFetch<{ data: { reply: string } }>('/api/v1/chat', {
method: 'POST',
body: { message },
token,
});