Refactor services
This commit is contained in:
parent
1ee59c0403
commit
e5c0825985
21 changed files with 322 additions and 516 deletions
|
|
@ -15,23 +15,11 @@ import { Project } from "./entities/Project";
|
|||
import { Task } from "./entities/Task";
|
||||
import { User } from "./entities/User";
|
||||
import { useStore } from "./store/useStore";
|
||||
import {
|
||||
fetchNotes,
|
||||
createNote,
|
||||
updateNote,
|
||||
fetchAreas,
|
||||
createArea,
|
||||
updateArea,
|
||||
fetchTags,
|
||||
createTag,
|
||||
updateTag,
|
||||
fetchProjects,
|
||||
createProject,
|
||||
updateProject,
|
||||
fetchTasks,
|
||||
createTask,
|
||||
updateTask,
|
||||
} from "./utils/apiService";
|
||||
import { fetchNotes, createNote, updateNote } from "./utils/notesService";
|
||||
import { fetchAreas, createArea, updateArea } from "./utils/areasService";
|
||||
import { fetchTags, createTag, updateTag } from "./utils/tagsService";
|
||||
import { fetchProjects, createProject, updateProject } from "./utils/projectsService";
|
||||
import { fetchTasks, createTask, updateTask } from "./utils/tasksService";
|
||||
|
||||
interface LayoutProps {
|
||||
currentUser: User;
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import {
|
|||
import ConfirmDialog from './Shared/ConfirmDialog';
|
||||
import AreaModal from './Area/AreaModal';
|
||||
import { useStore } from '../store/useStore';
|
||||
import { fetchAreas, createArea, updateArea, deleteArea } from '../utils/apiService';
|
||||
import { fetchAreas, createArea, updateArea, deleteArea } from '../utils/areasService';
|
||||
import { Area } from '../entities/Area';
|
||||
|
||||
const Areas: React.FC = () => {
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import { PencilSquareIcon, TrashIcon, TagIcon, DocumentTextIcon } from '@heroico
|
|||
import ConfirmDialog from '../Shared/ConfirmDialog';
|
||||
import NoteModal from './NoteModal';
|
||||
import { Note } from '../../entities/Note';
|
||||
import { fetchNotes, deleteNote as apiDeleteNote, updateNote as apiUpdateNote } from '../../utils/apiService';
|
||||
import { fetchNotes, deleteNote as apiDeleteNote, updateNote as apiUpdateNote } from '../../utils/notesService';
|
||||
|
||||
const NoteDetails: React.FC = () => {
|
||||
const { id } = useParams<{ id: string }>();
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { Note } from '../../entities/Note';
|
|||
import { useToast } from '../Shared/ToastContext';
|
||||
import TagInput from '../Tag/TagInput';
|
||||
import { Tag } from '../../entities/Tag';
|
||||
import { fetchTags } from '../../utils/apiService';
|
||||
import { fetchTags } from '../../utils/tagsService';
|
||||
|
||||
interface NoteModalProps {
|
||||
isOpen: boolean;
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ import {
|
|||
createNote,
|
||||
updateNote,
|
||||
deleteNote as apiDeleteNote,
|
||||
} from '../utils/apiService';
|
||||
} from '../utils/notesService';
|
||||
|
||||
const Notes: React.FC = () => {
|
||||
const [notes, setNotes] = useState<Note[]>([]);
|
||||
|
|
|
|||
|
|
@ -13,7 +13,11 @@ import { useStore } from "../../store/useStore";
|
|||
import NewTask from "../Task/NewTask";
|
||||
import { Project } from "../../entities/Project";
|
||||
import { PriorityType, Task } from "../../entities/Task";
|
||||
import { fetchProjectById, createTask, updateTask, deleteTask, updateProject, deleteProject, fetchAreas } from "../../utils/apiService";
|
||||
import { fetchProjectById, updateProject, deleteProject } from "../../utils/projectsService";
|
||||
import { createTask, updateTask, deleteTask } from "../../utils/tasksService";
|
||||
import { fetchAreas } from "../../utils/areasService";
|
||||
|
||||
|
||||
|
||||
type PriorityStyles = Record<PriorityType, string> & { default: string };
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import Switch from "../Shared/Switch";
|
|||
import { useStore } from "../../store/useStore";
|
||||
import {
|
||||
fetchTags,
|
||||
} from '../../utils/apiService';
|
||||
} from '../../utils/tagsService';
|
||||
|
||||
interface ProjectModalProps {
|
||||
isOpen: boolean;
|
||||
|
|
|
|||
|
|
@ -8,7 +8,9 @@ import {
|
|||
import ConfirmDialog from "./Shared/ConfirmDialog";
|
||||
import ProjectModal from "./Project/ProjectModal";
|
||||
import { useStore } from "../store/useStore";
|
||||
import { fetchProjects, createProject, updateProject, deleteProject, fetchAreas } from "../utils/apiService";
|
||||
import { fetchProjects, createProject, updateProject, deleteProject } from "../utils/projectsService";
|
||||
import { fetchAreas } from "../utils/areasService";
|
||||
|
||||
import { Project } from "../entities/Project";
|
||||
import { PriorityType, StatusType } from "../entities/Task";
|
||||
import { useSearchParams } from "react-router-dom";
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import { PencilSquareIcon, TrashIcon, TagIcon, MagnifyingGlassIcon } from '@hero
|
|||
import ConfirmDialog from './Shared/ConfirmDialog';
|
||||
import TagModal from './Tag/TagModal';
|
||||
import { Tag } from '../entities/Tag';
|
||||
import { fetchTags, createTag, updateTag, deleteTag as apiDeleteTag } from '../utils/apiService';
|
||||
import { fetchTags, createTag, updateTag, deleteTag as apiDeleteTag } from '../utils/tagsService';
|
||||
|
||||
const Tags: React.FC = () => {
|
||||
const [tags, setTags] = useState<Tag[]>([]);
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import { useToast } from "../Shared/ToastContext";
|
|||
import TagInput from "../Tag/TagInput";
|
||||
import { Project } from "../../entities/Project";
|
||||
import { useStore } from "../../store/useStore";
|
||||
import { fetchTags } from '../../utils/apiService';
|
||||
import { fetchTags } from '../../utils/tagsService';
|
||||
|
||||
interface TaskModalProps {
|
||||
isOpen: boolean;
|
||||
|
|
|
|||
|
|
@ -6,7 +6,8 @@ import {
|
|||
CalendarDaysIcon,
|
||||
ClockIcon,
|
||||
} from "@heroicons/react/24/outline";
|
||||
import { fetchTasks, updateTask, deleteTask, fetchProjects } from "../../utils/apiService";
|
||||
import { fetchTasks, updateTask, deleteTask } from "../../utils/tasksService";
|
||||
import { fetchProjects } from "../../utils/projectsService";
|
||||
import { Task } from "../../entities/Task";
|
||||
import { useStore } from "../../store/useStore";
|
||||
import TaskList from "./TaskList";
|
||||
|
|
|
|||
|
|
@ -1,53 +0,0 @@
|
|||
import { useState, useEffect } from 'react';
|
||||
|
||||
interface UseFetchResult<T> {
|
||||
data: T | null;
|
||||
loading: boolean;
|
||||
error: string | null;
|
||||
}
|
||||
|
||||
const useFetch = <T,>(url: string, options?: RequestInit): UseFetchResult<T> => {
|
||||
const [data, setData] = useState<T | null>(null);
|
||||
const [loading, setLoading] = useState<boolean>(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
let isMounted = true;
|
||||
const controller = new AbortController();
|
||||
|
||||
const fetchData = async () => {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
try {
|
||||
const response = await fetch(url, { ...options, signal: controller.signal });
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json();
|
||||
throw new Error(errorData.error || 'Failed to fetch data.');
|
||||
}
|
||||
const result: T = await response.json();
|
||||
if (isMounted) {
|
||||
setData(result);
|
||||
}
|
||||
} catch (err: any) {
|
||||
if (isMounted) {
|
||||
if (err.name !== 'AbortError') {
|
||||
setError(err.message);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
if (isMounted) setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
fetchData();
|
||||
|
||||
return () => {
|
||||
isMounted = false;
|
||||
controller.abort();
|
||||
};
|
||||
}, [url, JSON.stringify(options)]);
|
||||
|
||||
return { data, loading, error };
|
||||
};
|
||||
|
||||
export default useFetch;
|
||||
|
|
@ -1,69 +0,0 @@
|
|||
import useSWR from 'swr';
|
||||
import { Task } from '../entities/Task';
|
||||
|
||||
interface UseFetchTasksOptions {
|
||||
type?: string;
|
||||
tag?: string;
|
||||
}
|
||||
|
||||
interface Metrics {
|
||||
total_open_tasks: number;
|
||||
tasks_pending_over_month: number;
|
||||
tasks_in_progress_count: number;
|
||||
tasks_in_progress: Task[];
|
||||
tasks_due_today: Task[];
|
||||
suggested_tasks: Task[];
|
||||
}
|
||||
|
||||
interface UseFetchTasksResult {
|
||||
tasks: Task[];
|
||||
metrics: Metrics;
|
||||
isLoading: boolean;
|
||||
isError: boolean;
|
||||
mutate: () => void;
|
||||
}
|
||||
|
||||
const initialMetrics: Metrics = {
|
||||
total_open_tasks: 0,
|
||||
tasks_pending_over_month: 0,
|
||||
tasks_in_progress_count: 0,
|
||||
tasks_in_progress: [],
|
||||
tasks_due_today: [],
|
||||
suggested_tasks: [],
|
||||
};
|
||||
|
||||
const fetcher = (url: string) =>
|
||||
fetch(url, {
|
||||
credentials: 'include',
|
||||
headers: { Accept: 'application/json' },
|
||||
}).then((res) => {
|
||||
if (!res.ok) {
|
||||
throw new Error('Failed to fetch tasks.');
|
||||
}
|
||||
return res.json();
|
||||
});
|
||||
|
||||
const useFetchTasks = (options?: UseFetchTasksOptions): UseFetchTasksResult => {
|
||||
const params = new URLSearchParams();
|
||||
|
||||
if (options?.type) {
|
||||
params.append('type', options.type);
|
||||
}
|
||||
if (options?.tag) {
|
||||
params.append('tag', options.tag);
|
||||
}
|
||||
|
||||
const queryString = params.toString();
|
||||
const url = `/api/tasks${queryString ? `?${queryString}` : ''}`;
|
||||
const { data, error, mutate } = useSWR(url, fetcher);
|
||||
|
||||
return {
|
||||
tasks: data?.tasks || [],
|
||||
metrics: data?.metrics || initialMetrics,
|
||||
isLoading: !error && !data,
|
||||
isError: !!error,
|
||||
mutate,
|
||||
};
|
||||
};
|
||||
|
||||
export default useFetchTasks;
|
||||
|
|
@ -1,91 +0,0 @@
|
|||
import { useState } from 'react';
|
||||
import { Task } from '../entities/Task';
|
||||
|
||||
const useManageTasks = () => {
|
||||
const [tasks, setTasks] = useState<Task[]>([]);
|
||||
const [isLoading, setIsLoading] = useState<boolean>(false);
|
||||
const [isError, setIsError] = useState<boolean>(false);
|
||||
|
||||
const fetchTasks = async (query: string = '') => {
|
||||
setIsLoading(true);
|
||||
setIsError(false);
|
||||
try {
|
||||
const response = await fetch(`/api/tasks${query}`, {
|
||||
credentials: 'include',
|
||||
headers: { Accept: 'application/json' },
|
||||
});
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
setTasks(data);
|
||||
} else {
|
||||
throw new Error('Failed to fetch tasks.');
|
||||
}
|
||||
} catch (error) {
|
||||
setIsError(true);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const createTask = async (taskData: Partial<Task>) => {
|
||||
try {
|
||||
const response = await fetch('/api/task', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
credentials: 'include',
|
||||
body: JSON.stringify(taskData),
|
||||
});
|
||||
if (response.ok) {
|
||||
const newTask = await response.json();
|
||||
setTasks((prevTasks) => [newTask, ...prevTasks]);
|
||||
} else {
|
||||
throw new Error('Failed to create task.');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error creating task:', error);
|
||||
}
|
||||
};
|
||||
|
||||
const updateTask = async (taskId: number, taskData: Partial<Task>) => {
|
||||
try {
|
||||
const response = await fetch(`/api/task/${taskId}`, {
|
||||
method: 'PATCH',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
credentials: 'include',
|
||||
body: JSON.stringify(taskData),
|
||||
});
|
||||
if (response.ok) {
|
||||
const updatedTask = await response.json();
|
||||
setTasks((prevTasks) =>
|
||||
prevTasks.map((task) => (task.id === taskId ? updatedTask : task))
|
||||
);
|
||||
} else {
|
||||
throw new Error('Failed to update task.');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error updating task:', error);
|
||||
}
|
||||
};
|
||||
|
||||
const deleteTask = async (taskId: number) => {
|
||||
try {
|
||||
const response = await fetch(`/api/task/${taskId}`, {
|
||||
method: 'DELETE',
|
||||
credentials: 'include',
|
||||
});
|
||||
if (response.ok) {
|
||||
setTasks((prevTasks) => prevTasks.filter((task) => task.id !== taskId));
|
||||
} else {
|
||||
throw new Error('Failed to delete task.');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error deleting task:', error);
|
||||
}
|
||||
};
|
||||
|
||||
const mutateTasks = fetchTasks;
|
||||
|
||||
return { tasks, isLoading, isError, fetchTasks, mutateTasks, createTask, updateTask, deleteTask };
|
||||
};
|
||||
|
||||
export default useManageTasks;
|
||||
|
|
@ -1,255 +0,0 @@
|
|||
import { Project } from "../entities/Project";
|
||||
import { Area } from "../entities/Area";
|
||||
import { Note } from "../entities/Note";
|
||||
import { Task } from "../entities/Task";
|
||||
import { Tag } from "../entities/Tag";
|
||||
import { Metrics } from "../entities/Metrics";
|
||||
|
||||
/**
|
||||
* Projects API
|
||||
*/
|
||||
|
||||
export const fetchProjects = async (activeFilter = "all", areaFilter = ""): Promise<Project[]> => {
|
||||
let url = `/api/projects`;
|
||||
const params = new URLSearchParams();
|
||||
|
||||
if (activeFilter !== "all") params.append("active", activeFilter);
|
||||
if (areaFilter) params.append("area_id", areaFilter);
|
||||
if (params.toString()) url += `?${params.toString()}`;
|
||||
|
||||
const response = await fetch(url, {
|
||||
credentials: 'include',
|
||||
headers: { Accept: 'application/json' },
|
||||
});
|
||||
|
||||
if (!response.ok) throw new Error('Failed to fetch projects.');
|
||||
|
||||
const data = await response.json();
|
||||
return data.projects || data;
|
||||
};
|
||||
|
||||
export const fetchProjectById = async (projectId: string): Promise<Project> => {
|
||||
const response = await fetch(`/api/project/${projectId}`, {
|
||||
credentials: 'include',
|
||||
headers: { Accept: 'application/json' },
|
||||
});
|
||||
|
||||
if (!response.ok) throw new Error('Failed to fetch project details.');
|
||||
|
||||
return await response.json();
|
||||
};
|
||||
|
||||
export const createProject = async (projectData: Partial<Project>): Promise<Project> => {
|
||||
const response = await fetch('/api/project', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(projectData),
|
||||
});
|
||||
|
||||
if (!response.ok) throw new Error('Failed to create project.');
|
||||
|
||||
return await response.json();
|
||||
};
|
||||
|
||||
export const updateProject = async (projectId: number, projectData: Partial<Project>): Promise<Project> => {
|
||||
const response = await fetch(`/api/project/${projectId}`, {
|
||||
method: 'PATCH',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(projectData),
|
||||
});
|
||||
|
||||
if (!response.ok) throw new Error('Failed to update project.');
|
||||
|
||||
return await response.json();
|
||||
};
|
||||
|
||||
export const deleteProject = async (projectId: number): Promise<void> => {
|
||||
const response = await fetch(`/api/project/${projectId}`, {
|
||||
method: 'DELETE',
|
||||
});
|
||||
|
||||
if (!response.ok) throw new Error('Failed to delete project.');
|
||||
};
|
||||
|
||||
/**
|
||||
* Areas API
|
||||
*/
|
||||
|
||||
export const fetchAreas = async (): Promise<Area[]> => {
|
||||
const response = await fetch("/api/areas?active=true");
|
||||
if (!response.ok) throw new Error('Failed to fetch areas.');
|
||||
|
||||
return await response.json();
|
||||
};
|
||||
|
||||
export const createArea = async (areaData: Partial<Area>): Promise<Area> => {
|
||||
const response = await fetch('/api/areas', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(areaData),
|
||||
});
|
||||
|
||||
if (!response.ok) throw new Error('Failed to create area.');
|
||||
|
||||
return await response.json();
|
||||
};
|
||||
|
||||
export const updateArea = async (areaId: number, areaData: Partial<Area>): Promise<Area> => {
|
||||
const response = await fetch(`/api/areas/${areaId}`, {
|
||||
method: 'PATCH',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(areaData),
|
||||
});
|
||||
|
||||
if (!response.ok) throw new Error('Failed to update area.');
|
||||
|
||||
return await response.json();
|
||||
};
|
||||
|
||||
export const deleteArea = async (areaId: number): Promise<void> => {
|
||||
const response = await fetch(`/api/areas/${areaId}`, {
|
||||
method: 'DELETE',
|
||||
});
|
||||
|
||||
if (!response.ok) throw new Error('Failed to delete area.');
|
||||
};
|
||||
|
||||
/**
|
||||
* Notes API
|
||||
*/
|
||||
|
||||
export const fetchNotes = async (): Promise<Note[]> => {
|
||||
const response = await fetch("/api/notes");
|
||||
if (!response.ok) throw new Error('Failed to fetch notes.');
|
||||
|
||||
return await response.json();
|
||||
};
|
||||
|
||||
export const createNote = async (noteData: Note): Promise<Note> => {
|
||||
const response = await fetch('/api/notes', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(noteData),
|
||||
});
|
||||
|
||||
if (!response.ok) throw new Error('Failed to create note.');
|
||||
|
||||
return await response.json();
|
||||
};
|
||||
|
||||
export const updateNote = async (noteId: number, noteData: Note): Promise<Note> => {
|
||||
const response = await fetch(`/api/notes/${noteId}`, {
|
||||
method: 'PATCH',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(noteData),
|
||||
});
|
||||
|
||||
if (!response.ok) throw new Error('Failed to update note.');
|
||||
|
||||
return await response.json();
|
||||
};
|
||||
|
||||
export const deleteNote = async (noteId: number): Promise<void> => {
|
||||
const response = await fetch(`/api/notes/${noteId}`, {
|
||||
method: 'DELETE',
|
||||
});
|
||||
|
||||
if (!response.ok) throw new Error('Failed to delete note.');
|
||||
};
|
||||
|
||||
/**
|
||||
* Tasks API
|
||||
*/
|
||||
|
||||
export const fetchTasks = async (query = ''): Promise<{ tasks: Task[]; metrics: Metrics }> => {
|
||||
const response = await fetch(`/api/tasks${query}`);
|
||||
|
||||
if (!response.ok) throw new Error('Failed to fetch tasks.');
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (!Array.isArray(result.tasks)) {
|
||||
throw new Error('Resulting tasks are not an array.');
|
||||
}
|
||||
|
||||
if (!result.metrics) {
|
||||
throw new Error('Metrics data is not included.');
|
||||
}
|
||||
|
||||
return { tasks: result.tasks, metrics: result.metrics };
|
||||
};
|
||||
|
||||
export const createTask = async (taskData: Task): Promise<Task> => {
|
||||
const response = await fetch('/api/task', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(taskData),
|
||||
});
|
||||
|
||||
if (!response.ok) throw new Error('Failed to create task.');
|
||||
|
||||
return await response.json();
|
||||
};
|
||||
|
||||
export const updateTask = async (taskId: number, taskData: Task): Promise<Task> => {
|
||||
const response = await fetch(`/api/task/${taskId}`, {
|
||||
method: 'PATCH',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(taskData),
|
||||
});
|
||||
|
||||
if (!response.ok) throw new Error('Failed to update task.');
|
||||
|
||||
return await response.json();
|
||||
};
|
||||
|
||||
export const deleteTask = async (taskId: number): Promise<void> => {
|
||||
const response = await fetch(`/api/task/${taskId}`, {
|
||||
method: 'DELETE',
|
||||
});
|
||||
|
||||
if (!response.ok) throw new Error('Failed to delete task.');
|
||||
};
|
||||
|
||||
/**
|
||||
* Tags API
|
||||
*/
|
||||
|
||||
export const fetchTags = async (): Promise<Tag[]> => {
|
||||
const response = await fetch("/api/tags");
|
||||
if (!response.ok) throw new Error('Failed to fetch tags.');
|
||||
|
||||
return await response.json();
|
||||
};
|
||||
|
||||
export const createTag = async (tagData: Tag): Promise<Tag> => {
|
||||
const response = await fetch('/api/tag', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(tagData),
|
||||
});
|
||||
|
||||
if (!response.ok) throw new Error('Failed to create tag.');
|
||||
|
||||
return await response.json();
|
||||
};
|
||||
|
||||
export const updateTag = async (tagId: number, tagData: Tag): Promise<Tag> => {
|
||||
const response = await fetch(`/api/tag/${tagId}`, {
|
||||
method: 'PATCH',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(tagData),
|
||||
});
|
||||
|
||||
if (!response.ok) throw new Error('Failed to update tag.');
|
||||
|
||||
return await response.json();
|
||||
};
|
||||
|
||||
export const deleteTag = async (tagId: number): Promise<void> => {
|
||||
const response = await fetch(`/api/tag/${tagId}`, {
|
||||
method: 'DELETE',
|
||||
});
|
||||
|
||||
if (!response.ok) throw new Error('Failed to delete tag.');
|
||||
};
|
||||
40
app/frontend/utils/areasService.ts
Normal file
40
app/frontend/utils/areasService.ts
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
import { Area } from "../entities/Area";
|
||||
|
||||
export const fetchAreas = async (): Promise<Area[]> => {
|
||||
const response = await fetch("/api/areas?active=true");
|
||||
if (!response.ok) throw new Error('Failed to fetch areas.');
|
||||
|
||||
return await response.json();
|
||||
};
|
||||
|
||||
export const createArea = async (areaData: Partial<Area>): Promise<Area> => {
|
||||
const response = await fetch('/api/areas', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(areaData),
|
||||
});
|
||||
|
||||
if (!response.ok) throw new Error('Failed to create area.');
|
||||
|
||||
return await response.json();
|
||||
};
|
||||
|
||||
export const updateArea = async (areaId: number, areaData: Partial<Area>): Promise<Area> => {
|
||||
const response = await fetch(`/api/areas/${areaId}`, {
|
||||
method: 'PATCH',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(areaData),
|
||||
});
|
||||
|
||||
if (!response.ok) throw new Error('Failed to update area.');
|
||||
|
||||
return await response.json();
|
||||
};
|
||||
|
||||
export const deleteArea = async (areaId: number): Promise<void> => {
|
||||
const response = await fetch(`/api/areas/${areaId}`, {
|
||||
method: 'DELETE',
|
||||
});
|
||||
|
||||
if (!response.ok) throw new Error('Failed to delete area.');
|
||||
};
|
||||
40
app/frontend/utils/notesService.ts
Normal file
40
app/frontend/utils/notesService.ts
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
import { Note } from "../entities/Note";
|
||||
|
||||
export const fetchNotes = async (): Promise<Note[]> => {
|
||||
const response = await fetch("/api/notes");
|
||||
if (!response.ok) throw new Error('Failed to fetch notes.');
|
||||
|
||||
return await response.json();
|
||||
};
|
||||
|
||||
export const createNote = async (noteData: Note): Promise<Note> => {
|
||||
const response = await fetch('/api/notes', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(noteData),
|
||||
});
|
||||
|
||||
if (!response.ok) throw new Error('Failed to create note.');
|
||||
|
||||
return await response.json();
|
||||
};
|
||||
|
||||
export const updateNote = async (noteId: number, noteData: Note): Promise<Note> => {
|
||||
const response = await fetch(`/api/notes/${noteId}`, {
|
||||
method: 'PATCH',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(noteData),
|
||||
});
|
||||
|
||||
if (!response.ok) throw new Error('Failed to update note.');
|
||||
|
||||
return await response.json();
|
||||
};
|
||||
|
||||
export const deleteNote = async (noteId: number): Promise<void> => {
|
||||
const response = await fetch(`/api/notes/${noteId}`, {
|
||||
method: 'DELETE',
|
||||
});
|
||||
|
||||
if (!response.ok) throw new Error('Failed to delete note.');
|
||||
};
|
||||
63
app/frontend/utils/projectsService.ts
Normal file
63
app/frontend/utils/projectsService.ts
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
import { Project } from "../entities/Project";
|
||||
|
||||
export const fetchProjects = async (activeFilter = "all", areaFilter = ""): Promise<Project[]> => {
|
||||
let url = `/api/projects`;
|
||||
const params = new URLSearchParams();
|
||||
|
||||
if (activeFilter !== "all") params.append("active", activeFilter);
|
||||
if (areaFilter) params.append("area_id", areaFilter);
|
||||
if (params.toString()) url += `?${params.toString()}`;
|
||||
|
||||
const response = await fetch(url, {
|
||||
credentials: 'include',
|
||||
headers: { Accept: 'application/json' },
|
||||
});
|
||||
|
||||
if (!response.ok) throw new Error('Failed to fetch projects.');
|
||||
|
||||
const data = await response.json();
|
||||
return data.projects || data;
|
||||
};
|
||||
|
||||
export const fetchProjectById = async (projectId: string): Promise<Project> => {
|
||||
const response = await fetch(`/api/project/${projectId}`, {
|
||||
credentials: 'include',
|
||||
headers: { Accept: 'application/json' },
|
||||
});
|
||||
|
||||
if (!response.ok) throw new Error('Failed to fetch project details.');
|
||||
|
||||
return await response.json();
|
||||
};
|
||||
|
||||
export const createProject = async (projectData: Partial<Project>): Promise<Project> => {
|
||||
const response = await fetch('/api/project', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(projectData),
|
||||
});
|
||||
|
||||
if (!response.ok) throw new Error('Failed to create project.');
|
||||
|
||||
return await response.json();
|
||||
};
|
||||
|
||||
export const updateProject = async (projectId: number, projectData: Partial<Project>): Promise<Project> => {
|
||||
const response = await fetch(`/api/project/${projectId}`, {
|
||||
method: 'PATCH',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(projectData),
|
||||
});
|
||||
|
||||
if (!response.ok) throw new Error('Failed to update project.');
|
||||
|
||||
return await response.json();
|
||||
};
|
||||
|
||||
export const deleteProject = async (projectId: number): Promise<void> => {
|
||||
const response = await fetch(`/api/project/${projectId}`, {
|
||||
method: 'DELETE',
|
||||
});
|
||||
|
||||
if (!response.ok) throw new Error('Failed to delete project.');
|
||||
};
|
||||
40
app/frontend/utils/tagsService.ts
Normal file
40
app/frontend/utils/tagsService.ts
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
import { Tag } from "../entities/Tag";
|
||||
|
||||
export const fetchTags = async (): Promise<Tag[]> => {
|
||||
const response = await fetch("/api/tags");
|
||||
if (!response.ok) throw new Error('Failed to fetch tags.');
|
||||
|
||||
return await response.json();
|
||||
};
|
||||
|
||||
export const createTag = async (tagData: Tag): Promise<Tag> => {
|
||||
const response = await fetch('/api/tag', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(tagData),
|
||||
});
|
||||
|
||||
if (!response.ok) throw new Error('Failed to create tag.');
|
||||
|
||||
return await response.json();
|
||||
};
|
||||
|
||||
export const updateTag = async (tagId: number, tagData: Tag): Promise<Tag> => {
|
||||
const response = await fetch(`/api/tag/${tagId}`, {
|
||||
method: 'PATCH',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(tagData),
|
||||
});
|
||||
|
||||
if (!response.ok) throw new Error('Failed to update tag.');
|
||||
|
||||
return await response.json();
|
||||
};
|
||||
|
||||
export const deleteTag = async (tagId: number): Promise<void> => {
|
||||
const response = await fetch(`/api/tag/${tagId}`, {
|
||||
method: 'DELETE',
|
||||
});
|
||||
|
||||
if (!response.ok) throw new Error('Failed to delete tag.');
|
||||
};
|
||||
52
app/frontend/utils/tasksService.ts
Normal file
52
app/frontend/utils/tasksService.ts
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
import { Metrics } from "../entities/Metrics";
|
||||
import { Task } from "../entities/Task";
|
||||
|
||||
export const fetchTasks = async (query = ''): Promise<{ tasks: Task[]; metrics: Metrics }> => {
|
||||
const response = await fetch(`/api/tasks${query}`);
|
||||
|
||||
if (!response.ok) throw new Error('Failed to fetch tasks.');
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (!Array.isArray(result.tasks)) {
|
||||
throw new Error('Resulting tasks are not an array.');
|
||||
}
|
||||
|
||||
if (!result.metrics) {
|
||||
throw new Error('Metrics data is not included.');
|
||||
}
|
||||
|
||||
return { tasks: result.tasks, metrics: result.metrics };
|
||||
};
|
||||
|
||||
export const createTask = async (taskData: Task): Promise<Task> => {
|
||||
const response = await fetch('/api/task', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(taskData),
|
||||
});
|
||||
|
||||
if (!response.ok) throw new Error('Failed to create task.');
|
||||
|
||||
return await response.json();
|
||||
};
|
||||
|
||||
export const updateTask = async (taskId: number, taskData: Task): Promise<Task> => {
|
||||
const response = await fetch(`/api/task/${taskId}`, {
|
||||
method: 'PATCH',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(taskData),
|
||||
});
|
||||
|
||||
if (!response.ok) throw new Error('Failed to update task.');
|
||||
|
||||
return await response.json();
|
||||
};
|
||||
|
||||
export const deleteTask = async (taskId: number): Promise<void> => {
|
||||
const response = await fetch(`/api/task/${taskId}`, {
|
||||
method: 'DELETE',
|
||||
});
|
||||
|
||||
if (!response.ok) throw new Error('Failed to delete task.');
|
||||
};
|
||||
File diff suppressed because one or more lines are too long
Loading…
Add table
Add a link
Reference in a new issue