creates application
This commit is contained in:
25
frontend/src/api/auth.ts
Normal file
25
frontend/src/api/auth.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import axios from 'axios';
|
||||
import type { User } from '@/types';
|
||||
|
||||
const AUTH_BASE = '/auth';
|
||||
|
||||
export const authApi = {
|
||||
login: (): void => {
|
||||
window.location.href = `${AUTH_BASE}/login`;
|
||||
},
|
||||
|
||||
logout: async (): Promise<void> => {
|
||||
await axios.post(`${AUTH_BASE}/logout`, {}, { withCredentials: true });
|
||||
},
|
||||
|
||||
getCurrentUser: async (): Promise<User | null> => {
|
||||
try {
|
||||
const { data } = await axios.get<User>(`${AUTH_BASE}/me`, {
|
||||
withCredentials: true,
|
||||
});
|
||||
return data;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
};
|
||||
26
frontend/src/api/client.ts
Normal file
26
frontend/src/api/client.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import axios, { AxiosError } from 'axios';
|
||||
|
||||
const apiClient = axios.create({
|
||||
baseURL: '/api',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
withCredentials: true,
|
||||
});
|
||||
|
||||
// Response interceptor for error handling
|
||||
apiClient.interceptors.response.use(
|
||||
(response) => response,
|
||||
(error: AxiosError<{ error?: string; details?: unknown }>) => {
|
||||
if (error.response?.status === 401) {
|
||||
// Redirect to login on 401
|
||||
window.location.href = '/login';
|
||||
return Promise.reject(error);
|
||||
}
|
||||
|
||||
const message = error.response?.data?.error || error.message || 'An error occurred';
|
||||
return Promise.reject(new Error(message));
|
||||
}
|
||||
);
|
||||
|
||||
export default apiClient;
|
||||
23
frontend/src/api/clients.ts
Normal file
23
frontend/src/api/clients.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import apiClient from './client';
|
||||
import type { Client, CreateClientInput, UpdateClientInput } from '@/types';
|
||||
|
||||
export const clientsApi = {
|
||||
getAll: async (): Promise<Client[]> => {
|
||||
const { data } = await apiClient.get<Client[]>('/clients');
|
||||
return data;
|
||||
},
|
||||
|
||||
create: async (input: CreateClientInput): Promise<Client> => {
|
||||
const { data } = await apiClient.post<Client>('/clients', input);
|
||||
return data;
|
||||
},
|
||||
|
||||
update: async (id: string, input: UpdateClientInput): Promise<Client> => {
|
||||
const { data } = await apiClient.put<Client>(`/clients/${id}`, input);
|
||||
return data;
|
||||
},
|
||||
|
||||
delete: async (id: string): Promise<void> => {
|
||||
await apiClient.delete(`/clients/${id}`);
|
||||
},
|
||||
};
|
||||
25
frontend/src/api/projects.ts
Normal file
25
frontend/src/api/projects.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import apiClient from './client';
|
||||
import type { Project, CreateProjectInput, UpdateProjectInput } from '@/types';
|
||||
|
||||
export const projectsApi = {
|
||||
getAll: async (clientId?: string): Promise<Project[]> => {
|
||||
const { data } = await apiClient.get<Project[]>('/projects', {
|
||||
params: clientId ? { clientId } : undefined,
|
||||
});
|
||||
return data;
|
||||
},
|
||||
|
||||
create: async (input: CreateProjectInput): Promise<Project> => {
|
||||
const { data } = await apiClient.post<Project>('/projects', input);
|
||||
return data;
|
||||
},
|
||||
|
||||
update: async (id: string, input: UpdateProjectInput): Promise<Project> => {
|
||||
const { data } = await apiClient.put<Project>(`/projects/${id}`, input);
|
||||
return data;
|
||||
},
|
||||
|
||||
delete: async (id: string): Promise<void> => {
|
||||
await apiClient.delete(`/projects/${id}`);
|
||||
},
|
||||
};
|
||||
31
frontend/src/api/timeEntries.ts
Normal file
31
frontend/src/api/timeEntries.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import apiClient from './client';
|
||||
import type {
|
||||
TimeEntry,
|
||||
PaginatedTimeEntries,
|
||||
CreateTimeEntryInput,
|
||||
UpdateTimeEntryInput,
|
||||
TimeEntryFilters,
|
||||
} from '@/types';
|
||||
|
||||
export const timeEntriesApi = {
|
||||
getAll: async (filters?: TimeEntryFilters): Promise<PaginatedTimeEntries> => {
|
||||
const { data } = await apiClient.get<PaginatedTimeEntries>('/time-entries', {
|
||||
params: filters,
|
||||
});
|
||||
return data;
|
||||
},
|
||||
|
||||
create: async (input: CreateTimeEntryInput): Promise<TimeEntry> => {
|
||||
const { data } = await apiClient.post<TimeEntry>('/time-entries', input);
|
||||
return data;
|
||||
},
|
||||
|
||||
update: async (id: string, input: UpdateTimeEntryInput): Promise<TimeEntry> => {
|
||||
const { data } = await apiClient.put<TimeEntry>(`/time-entries/${id}`, input);
|
||||
return data;
|
||||
},
|
||||
|
||||
delete: async (id: string): Promise<void> => {
|
||||
await apiClient.delete(`/time-entries/${id}`);
|
||||
},
|
||||
};
|
||||
30
frontend/src/api/timer.ts
Normal file
30
frontend/src/api/timer.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import apiClient from './client';
|
||||
import type { OngoingTimer, TimeEntry } from '@/types';
|
||||
|
||||
export const timerApi = {
|
||||
getOngoing: async (): Promise<OngoingTimer | null> => {
|
||||
const { data } = await apiClient.get<OngoingTimer | null>('/timer');
|
||||
return data;
|
||||
},
|
||||
|
||||
start: async (projectId?: string): Promise<OngoingTimer> => {
|
||||
const { data } = await apiClient.post<OngoingTimer>('/timer/start', {
|
||||
projectId,
|
||||
});
|
||||
return data;
|
||||
},
|
||||
|
||||
update: async (projectId?: string | null): Promise<OngoingTimer> => {
|
||||
const { data } = await apiClient.put<OngoingTimer>('/timer', {
|
||||
projectId,
|
||||
});
|
||||
return data;
|
||||
},
|
||||
|
||||
stop: async (projectId?: string): Promise<TimeEntry> => {
|
||||
const { data } = await apiClient.post<TimeEntry>('/timer/stop', {
|
||||
projectId,
|
||||
});
|
||||
return data;
|
||||
},
|
||||
};
|
||||
Reference in New Issue
Block a user