Connect the website to the API
This commit is contained in:
parent
ab791efe42
commit
32e8dfa4bb
@ -1,34 +1,38 @@
|
||||
import { DateTime } from "luxon";
|
||||
import { APIError, Task } from "./types";
|
||||
|
||||
export interface Task {
|
||||
id: number;
|
||||
name: string;
|
||||
due: DateTime;
|
||||
}
|
||||
export const getTasks = async (
|
||||
limit: number = 10,
|
||||
offset: number = 0
|
||||
): Promise<{ items: Task[]; count: number }> => {
|
||||
const url = new URL("tasks", API_URL);
|
||||
url.searchParams.set("limit", limit.toString());
|
||||
url.searchParams.set("offset", offset.toString());
|
||||
|
||||
const tasks: Task[] = [...Array(5).keys()].map(n => ({
|
||||
id: n,
|
||||
name: `Task ${n}`,
|
||||
due: DateTime.local().plus({ weeks: n })
|
||||
}));
|
||||
const response = await fetch(url.toString());
|
||||
if (!response.ok) {
|
||||
throw new APIError("Task fetch failed");
|
||||
}
|
||||
|
||||
export const getTasks = async (): Promise<Task[]> =>
|
||||
new Promise<Task[]>((resolve, reject) =>
|
||||
setTimeout(() => resolve(tasks), 3000)
|
||||
);
|
||||
const json = await response.json();
|
||||
if (!Number.isSafeInteger(json.count) || !Array.isArray(json.tasks)) {
|
||||
throw new APIError("Invalid response from server.");
|
||||
}
|
||||
const count: number = json.count;
|
||||
const items: Task[] = (json.tasks as any[]).map(t => {
|
||||
if (
|
||||
typeof t !== "object" ||
|
||||
!Number.isSafeInteger(t.id) ||
|
||||
typeof t.name !== "string"
|
||||
) {
|
||||
throw new APIError("Invalid response from server.");
|
||||
}
|
||||
|
||||
export const schedule = async (task: Omit<Task, "id">): Promise<Task> =>
|
||||
new Promise<Task>((resolve, reject) =>
|
||||
setTimeout(() => {
|
||||
const id = Math.max(...tasks.map(t => t.id)) + 1;
|
||||
tasks[id] = { ...task, id };
|
||||
resolve(tasks[id]);
|
||||
}, 3000)
|
||||
);
|
||||
return {
|
||||
id: t.id,
|
||||
name: t.name,
|
||||
schedule: { type: "Plain" }
|
||||
};
|
||||
});
|
||||
|
||||
export const unschedule = async (taskId: number): Promise<void> =>
|
||||
new Promise<void>((resolve, reject) =>
|
||||
setTimeout(() => {
|
||||
delete tasks[taskId];
|
||||
}, 3000)
|
||||
);
|
||||
return { items, count };
|
||||
};
|
||||
|
8
src/frontend/api/types.ts
Normal file
8
src/frontend/api/types.ts
Normal file
@ -0,0 +1,8 @@
|
||||
export class APIError extends Error {}
|
||||
export interface Task {
|
||||
id: number;
|
||||
name: string;
|
||||
schedule: {
|
||||
type: "Plain";
|
||||
};
|
||||
}
|
1
src/frontend/globals.d.ts
vendored
Normal file
1
src/frontend/globals.d.ts
vendored
Normal file
@ -0,0 +1 @@
|
||||
declare const API_URL: string;
|
@ -1,5 +1,6 @@
|
||||
import { getTasks } from "@kredens/frontend/api/tasks";
|
||||
import { Task as APITask } from "@kredens/frontend/api/types";
|
||||
import { all, call, put, takeEvery } from "redux-saga/effects";
|
||||
import { getTasks, Task as APITask } from "../api/tasks";
|
||||
import { taskFetchError, taskFetchOk, taskFetchStart } from "./tasks/actions";
|
||||
import { Task, TaskQuery, TaskScheduleType } from "./tasks/types";
|
||||
|
||||
@ -7,22 +8,26 @@ export function* fetchTasksSaga(query: TaskQuery = { limit: 10 }) {
|
||||
yield put(taskFetchStart(query));
|
||||
|
||||
try {
|
||||
const tasks: APITask[] = yield call(getTasks);
|
||||
const tasks: { items: APITask[]; count: number } = yield call(
|
||||
getTasks,
|
||||
query.limit,
|
||||
query.offset
|
||||
);
|
||||
yield put(
|
||||
taskFetchOk(
|
||||
query,
|
||||
tasks
|
||||
tasks.items
|
||||
.map<[string, Task]>(t => [
|
||||
t.id.toString(),
|
||||
{
|
||||
name: t.name,
|
||||
schedule: {
|
||||
type: TaskScheduleType.Once,
|
||||
due: t.due.toISO()
|
||||
type: TaskScheduleType[t.schedule.type]
|
||||
}
|
||||
}
|
||||
])
|
||||
.reduce((res, [id, task]) => ({ ...res, [id]: task }), {})
|
||||
.reduce((res, [id, task]) => ({ ...res, [id]: task }), {}),
|
||||
tasks.count
|
||||
)
|
||||
);
|
||||
} catch (error) {
|
||||
|
@ -26,12 +26,14 @@ export function taskFetchStart(query: TaskQuery): TasksActionType {
|
||||
|
||||
export function taskFetchOk(
|
||||
query: TaskQuery,
|
||||
results: { [key: string]: Task }
|
||||
results: { [key: string]: Task },
|
||||
count: number
|
||||
): TasksActionType {
|
||||
return {
|
||||
type: TasksAction.TASKS_FETCH_OK,
|
||||
query,
|
||||
results,
|
||||
count,
|
||||
fetched: DateTime.utc().toISO()
|
||||
};
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ import { TasksAction, TasksActionType, TasksState } from "./types";
|
||||
|
||||
const initialState: TasksState = {
|
||||
items: {},
|
||||
count: 0,
|
||||
queries: {}
|
||||
};
|
||||
|
||||
|
@ -21,8 +21,8 @@ export interface Task {
|
||||
|
||||
export interface TaskQuery {
|
||||
query?: string;
|
||||
after?: string;
|
||||
limit: number;
|
||||
offset?: number;
|
||||
}
|
||||
|
||||
interface TaskQueryOk {
|
||||
@ -48,6 +48,7 @@ export interface TasksState {
|
||||
items: {
|
||||
[key: string]: Task;
|
||||
};
|
||||
count: number;
|
||||
queries: {
|
||||
[key: string]: TaskQueryResult;
|
||||
};
|
||||
@ -82,6 +83,7 @@ interface TaskFetchOkAction {
|
||||
type: TasksAction.TASKS_FETCH_OK;
|
||||
query: TaskQuery;
|
||||
results: { [key: string]: Task };
|
||||
count: number;
|
||||
fetched: string;
|
||||
}
|
||||
|
||||
|
@ -24,7 +24,12 @@ const config: webpack.Configuration = {
|
||||
alias: {
|
||||
"@kredens": path.resolve(__dirname, "src/")
|
||||
}
|
||||
}
|
||||
},
|
||||
plugins: [
|
||||
new webpack.DefinePlugin({
|
||||
API_URL: JSON.stringify("http://localhost:3000/api/")
|
||||
})
|
||||
]
|
||||
};
|
||||
|
||||
export default config;
|
||||
|
Loading…
x
Reference in New Issue
Block a user