diff --git a/frontend/helpers/api/api.ts b/frontend/helpers/api/api.ts deleted file mode 100644 index 981e2c0da..000000000 --- a/frontend/helpers/api/api.ts +++ /dev/null @@ -1,215 +0,0 @@ -import axiosModule, { AxiosRequestConfig } from "axios" - -export type AccessToken = string - -export interface User { - active: boolean - role: string - email: string - emailConfirmedAt?: string - firstName?: string - lastName?: string - phoneNumber?: string -} - -export interface NewUser { - email: string - password: string - firstName?: string - lastName?: string - phoneNumber?: string -} - -export interface LoginCredentials { - email: string - password: string -} - -export interface ForgotPassword { - email: string -} - -export interface ResetPasswordRequest extends AuthenticatedRequest { - accessToken: string - password: string -} - -export interface ResetPasswordResponse { - message: string -} - -export interface Source { - name?: string - id?: number - url?: string - contact_email?: string -} - -export interface Perpetrator { - first_name?: string - last_name?: string -} - -export enum Rank { - TECHNICIAN = "Technician", - OFFICER = "Officer", - DETECTIVE = "Detective", - CORPORAL = "Corporal", - SERGEANT = "Sergeant", - LIEUTENANT = "Lieutenant", - CAPTAIN = "Captain", - DEPUTY = "Deputy", - CHIEF = "Chief" -} - -export interface Officer { - id?: number - first_name?: string - last_name?: string - race?: string - ethnicity?: string - gender?: string - rank?: Rank - star?: string - date_of_birth?: Date -} - -export interface UseOfForce { - item?: string -} -export interface Incident { - id: number - source?: Source - source_id?: number - location?: string - locationLonLat?: [number, number] //TODO: Backend data does not return locationLonLat attribute. Remove this and refactor frontend - latitude?: number - longitude?: number - time_of_incident?: string - department?: string - perpetrators: Perpetrator[] - description?: string - use_of_force?: UseOfForce[] -} - -interface AuthenticatedRequest { - accessToken: AccessToken -} - -export type RegisterRequest = NewUser -export type LoginRequest = LoginCredentials -export type WhoamiRequest = AuthenticatedRequest -export interface IncidentSearchRequest extends AuthenticatedRequest { - description?: string - dateStart?: string - dateEnd?: string - location?: string - source?: string - page?: number - perPage?: number -} - -export type IncidentSearchResponse = { - results: Incident[] - page: number - totalPages: number - totalResults: number -} - -export const baseURL = process.env.NEXT_PUBLIC_API_BASE_URL || "http://localhost:5000/api/v1" - -const axios = axiosModule.create({ - baseURL, - timeout: 5000 -}) - -export function login(data: LoginRequest): Promise { - return request({ - url: "/auth/login", - method: "POST", - data - }).then(({ access_token }) => access_token) -} - -export function register(data: RegisterRequest): Promise { - return request({ - url: "/auth/register", - method: "POST", - data - }).then(({ access_token }) => access_token) -} - -export function forgotPassowrd(data: ForgotPassword): Promise { - return request({ - url: "/auth/forgotPassword", - method: "POST", - data - }) -} - -export function resetPassword(req: ResetPasswordRequest): Promise { - const { accessToken } = req - - return request({ - url: `/auth/resetPassword`, - method: "POST", - data: { password: req.password }, - accessToken - }) -} - -export function whoami({ accessToken }: WhoamiRequest): Promise { - return request({ - url: "/auth/whoami", - method: "GET", - accessToken - }).then(({ active, email, email_confirmed_at, first_name, last_name, phone_number, role }) => ({ - active, - email, - emailConfirmedAt: email_confirmed_at, - firstName: first_name, - lastName: last_name, - phoneNumber: phone_number, - role: role - })) -} - -export function searchIncidents({ - accessToken, - dateStart, - dateEnd, - ...rest -}: IncidentSearchRequest): Promise { - if (dateStart) dateStart = new Date(dateStart).toISOString().slice(0, -1) - if (dateEnd) dateEnd = new Date(dateEnd).toISOString().slice(0, -1) - - return request({ - url: "/incidents/search", - method: "POST", - accessToken, - data: { dateStart, dateEnd, ...rest } - }) -} - -export async function getIncidentById(id: number, accessToken: string): Promise { - return request({ - url: `/incidents/get/${id}`, - method: "GET", - accessToken - }) -} - -function request({ accessToken, ...config }: AxiosRequestConfig & { accessToken?: AccessToken }) { - let { headers, ...rest } = config - if (accessToken) { - headers = { - Authorization: `Bearer ${accessToken}`, - ...headers - } - } - - return axios({ - headers, - ...rest - }).then((response) => response.data) -} diff --git a/frontend/helpers/api/auth/auth.ts b/frontend/helpers/api/auth/auth.ts new file mode 100644 index 000000000..169e377df --- /dev/null +++ b/frontend/helpers/api/auth/auth.ts @@ -0,0 +1,61 @@ +import { request, AccessToken } from "../base" +import { + User, + RegisterRequest, + LoginRequest, + ForgotPassword, + ResetPasswordRequest, + ResetPasswordResponse, + WhoamiRequest +} from "./types" + +export function login(data: LoginRequest): Promise { + return request({ + url: "/auth/login", + method: "POST", + data + }).then(({ access_token }) => access_token) +} + +export function register(data: RegisterRequest): Promise { + return request({ + url: "/auth/register", + method: "POST", + data + }).then(({ access_token }) => access_token) +} + +export function forgotPassowrd(data: ForgotPassword): Promise { + return request({ + url: "/auth/forgotPassword", + method: "POST", + data + }) +} + +export function resetPassword(req: ResetPasswordRequest): Promise { + const { accessToken } = req + + return request({ + url: `/auth/resetPassword`, + method: "POST", + data: { password: req.password }, + accessToken + }) +} + +export function whoami({ accessToken }: WhoamiRequest): Promise { + return request({ + url: "/auth/whoami", + method: "GET", + accessToken + }).then(({ active, email, email_confirmed_at, first_name, last_name, phone_number, role }) => ({ + active, + email, + emailConfirmedAt: email_confirmed_at, + firstName: first_name, + lastName: last_name, + phoneNumber: phone_number, + role: role + })) +} diff --git a/frontend/helpers/api/auth/index.ts b/frontend/helpers/api/auth/index.ts new file mode 100644 index 000000000..6f7e0d0be --- /dev/null +++ b/frontend/helpers/api/auth/index.ts @@ -0,0 +1,2 @@ +export * from "./types" +export * from "./auth" diff --git a/frontend/helpers/api/auth/types.ts b/frontend/helpers/api/auth/types.ts new file mode 100644 index 000000000..e5f735ed3 --- /dev/null +++ b/frontend/helpers/api/auth/types.ts @@ -0,0 +1,41 @@ +import { AuthenticatedRequest } from "../base" + +export interface User { + active: boolean + role: string + email: string + emailConfirmedAt?: string + firstName?: string + lastName?: string + phoneNumber?: string +} + +export interface NewUser { + email: string + password: string + firstName?: string + lastName?: string + phoneNumber?: string +} + +export interface LoginCredentials { + email: string + password: string +} + +export interface ForgotPassword { + email: string +} + +export interface ResetPasswordRequest extends AuthenticatedRequest { + accessToken: string + password: string +} + +export interface ResetPasswordResponse { + message: string +} + +export type RegisterRequest = NewUser +export type LoginRequest = LoginCredentials +export type WhoamiRequest = AuthenticatedRequest diff --git a/frontend/helpers/api/base.ts b/frontend/helpers/api/base.ts new file mode 100644 index 000000000..43ecf4c54 --- /dev/null +++ b/frontend/helpers/api/base.ts @@ -0,0 +1,31 @@ +import axiosModule, { AxiosRequestConfig } from "axios" +import { baseURL } from "./config" + +export type AccessToken = string + +export interface AuthenticatedRequest { + accessToken: AccessToken +} + +const axios = axiosModule.create({ + baseURL, + timeout: 5000 +}) + +export function request({ + accessToken, + ...config +}: AxiosRequestConfig & { accessToken?: AccessToken }) { + let { headers, ...rest } = config + if (accessToken) { + headers = { + Authorization: `Bearer ${accessToken}`, + ...headers + } + } + + return axios({ + headers, + ...rest + }).then((response) => response.data) +} diff --git a/frontend/helpers/api/config.ts b/frontend/helpers/api/config.ts new file mode 100644 index 000000000..d0de1afbe --- /dev/null +++ b/frontend/helpers/api/config.ts @@ -0,0 +1 @@ +export const baseURL = process.env.NEXT_PUBLIC_API_BASE_URL || "http://localhost:5000/api/v1" diff --git a/frontend/helpers/api/incidents/incidents.ts b/frontend/helpers/api/incidents/incidents.ts new file mode 100644 index 000000000..1497d89b8 --- /dev/null +++ b/frontend/helpers/api/incidents/incidents.ts @@ -0,0 +1,27 @@ +import { AccessToken, request } from "../base" +import { Incident, IncidentSearchRequest, IncidentSearchResponse } from "./types" + +export function searchIncidents({ + accessToken, + dateStart, + dateEnd, + ...rest +}: IncidentSearchRequest): Promise { + if (dateStart) dateStart = new Date(dateStart).toISOString().slice(0, -1) + if (dateEnd) dateEnd = new Date(dateEnd).toISOString().slice(0, -1) + + return request({ + url: "/incidents/search", + method: "POST", + accessToken, + data: { dateStart, dateEnd, ...rest } + }) +} + +export async function getIncidentById(id: number, accessToken: AccessToken): Promise { + return request({ + url: `/incidents/get/${id}`, + method: "GET", + accessToken + }) +} diff --git a/frontend/helpers/api/incidents/index.ts b/frontend/helpers/api/incidents/index.ts new file mode 100644 index 000000000..4708ead6a --- /dev/null +++ b/frontend/helpers/api/incidents/index.ts @@ -0,0 +1,2 @@ +export * from "./types" +export * from "./incidents" diff --git a/frontend/helpers/api/incidents/types.ts b/frontend/helpers/api/incidents/types.ts new file mode 100644 index 000000000..184966b92 --- /dev/null +++ b/frontend/helpers/api/incidents/types.ts @@ -0,0 +1,73 @@ +import { AuthenticatedRequest } from "../base" + +export interface Source { + name?: string + id?: number + url?: string + contact_email?: string +} + +export interface Perpetrator { + first_name?: string + last_name?: string +} + +export enum Rank { + TECHNICIAN = "Technician", + OFFICER = "Officer", + DETECTIVE = "Detective", + CORPORAL = "Corporal", + SERGEANT = "Sergeant", + LIEUTENANT = "Lieutenant", + CAPTAIN = "Captain", + DEPUTY = "Deputy", + CHIEF = "Chief" +} + +export interface Officer { + id?: number + first_name?: string + last_name?: string + race?: string + ethnicity?: string + gender?: string + rank?: Rank + star?: string + date_of_birth?: Date +} + +export interface UseOfForce { + item?: string +} + +export interface Incident { + id: number + source?: Source + source_id?: number + location?: string + locationLonLat?: [number, number] //TODO: Backend data does not return locationLonLat attribute. Remove this and refactor frontend + latitude?: number + longitude?: number + time_of_incident?: string + department?: string + perpetrators: Perpetrator[] + description?: string + use_of_force?: UseOfForce[] +} + +export interface IncidentSearchRequest extends AuthenticatedRequest { + description?: string + dateStart?: string + dateEnd?: string + location?: string + source?: string + page?: number + perPage?: number +} + +export type IncidentSearchResponse = { + results: Incident[] + page: number + totalPages: number + totalResults: number +} diff --git a/frontend/helpers/api/index.ts b/frontend/helpers/api/index.ts index 502ce65f5..fe98c5125 100644 --- a/frontend/helpers/api/index.ts +++ b/frontend/helpers/api/index.ts @@ -1,2 +1,5 @@ -export * from "./api" +export * from "./base" +export * from "./config" +export * from "./auth" +export * from "./incidents" export { useMockServiceWorker, apiMode } from "./mocks/browser.setup"