Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions src/apiClient/approvals.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@ import { toIsoDate } from "src/lib/fmtDate";

import { request } from "./client";
import { GearTypeWithShorthand } from "./gear";
import { ApprovalID, GearItemID, PersonID } from "./idTypes";
import { PersonBase } from "./people";

export type Approval = {
id: number;
id: ApprovalID;
startDate: string;
endDate: string;
note: string;
Expand All @@ -17,7 +18,7 @@ export type Approval = {
export type RenterApproval = Omit<Approval, "renter">;

interface GearItem {
id: string;
id: GearItemID;
type: GearTypeWithShorthand;
}

Expand Down Expand Up @@ -57,7 +58,7 @@ export type CreateNewApprovalArgs = {
startDate: Date;
endDate: Date;
note?: string;
renter: string;
renter: PersonID;
items: ApprovalItemToCreate[];
};

Expand Down
29 changes: 15 additions & 14 deletions src/apiClient/gear.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { request } from "./client";
import { GearItemID, GearTypeID, LocationID, PurchasableID } from "./idTypes";
import { PersonBase, PersonSummary } from "./people";
import { ListWrapper, Note } from "./types";

export interface GearSummary {
id: string;
id: GearItemID;
available: boolean;
broken: string;
// TODO: This is weird, we shouldn't have the rentals in there
Expand All @@ -19,7 +20,7 @@ export interface GearSummary {
type: GearTypeWithFee;
picture?: string;
location: {
id: number;
id: LocationID;
shorthand: string;
};
}
Expand All @@ -36,14 +37,14 @@ export interface GearRental {
}

export interface PurchasableItem {
id: string;
id: PurchasableID;
price: number;
name: string;
}

/** The minimal representation of a gear type*/
export interface GearTypeBase {
id: number;
id: GearTypeID;
typeName: string;
}

Expand All @@ -63,49 +64,49 @@ export interface GearType extends GearTypeWithShorthand {
}

export interface GearLocation {
id: number;
id: LocationID;
shorthand: string;
}

async function getGearRentalHistory(
id: string,
id: GearItemID,
page?: number,
): Promise<ListWrapper<GearRental>> {
return request(`/gear/${id}/rentals/`, "GET", { ...(page && { page }) });
}

async function addNote(id: string, note: string) {
async function addNote(id: GearItemID, note: string) {
return request(`/gear/${id}/note/`, "POST", {
note,
});
}

async function markRetired(id: string, note?: string) {
async function markRetired(id: GearItemID, note?: string) {
return request(`/gear/${id}/retired/`, "POST", {
note,
});
}
async function markBroken(id: string, note: string) {
async function markBroken(id: GearItemID, note: string) {
return request(`/gear/${id}/broken/`, "POST", {
note,
});
}
async function markMissing(id: string, note?: string) {
async function markMissing(id: GearItemID, note?: string) {
return request(`/gear/${id}/missing/`, "POST", {
note,
});
}
async function markUnretired(id: string, note?: string) {
async function markUnretired(id: GearItemID, note?: string) {
return request(`/gear/${id}/retired/`, "DELETE", {
note,
});
}
async function markFixed(id: string, note?: string) {
async function markFixed(id: GearItemID, note?: string) {
return request(`/gear/${id}/broken/`, "DELETE", {
note,
});
}
async function markFound(id: string, note?: string) {
async function markFound(id: GearItemID, note?: string) {
return request(`/gear/${id}/missing/`, "DELETE", {
note,
});
Expand All @@ -129,7 +130,7 @@ async function createGear(
}

async function editGearItem(
id: string,
id: GearItemID,
item: {
specification?: string;
description?: string;
Expand Down
16 changes: 16 additions & 0 deletions src/apiClient/idTypes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Branded types improve type safety by differenciating ID-types that have the same
// run-time type but represent different entities.
declare const __brand: unique symbol;

export type PersonID = number & { readonly [__brand]: "PersonID" };
export type GearItemID = string & { readonly [__brand]: "GearItemID" };
export type PeopleGroupID = number & { readonly [__brand]: "PeopleGroupID" };
export type ApprovalID = number & { readonly [__brand]: "ApprovalID" };
export type LocationID = number & { readonly [__brand]: "LocationID" };
export type GearTypeID = number & { readonly [__brand]: "GearTypeID" };
export type PurchasableID = number & { readonly [__brand]: "PurchasableID" };
export type OfficeHourID = number & { readonly [__brand]: "OfficeHourID" };
export type SignupID = number & { readonly [__brand]: "SignupID" };
export type GearNoteID = number & { readonly [__brand]: "GearNoteID" };
export type PersonNoteID = number & { readonly [__brand]: "PersonNoteID" };
export type NoteID = GearNoteID | PersonNoteID;
5 changes: 3 additions & 2 deletions src/apiClient/officeHours.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { request } from "./client";
import { OfficeHourID, SignupID } from "./idTypes";

export async function signUp(officeHourId: string) {
export async function signUp(officeHourId: OfficeHourID) {
return request(`/office-hours/${officeHourId}/signup/`, "POST");
}

export async function cancelSignUp(signupId: string) {
export async function cancelSignUp(signupId: SignupID) {
return request(`/office-hour-signups/${signupId}/`, "DELETE");
}

Expand Down
39 changes: 17 additions & 22 deletions src/apiClient/people.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@ import dayjs from "dayjs";

import { request } from "./client";
import { GearTypeWithFee } from "./gear";
import { GearItemID, PeopleGroupID, PersonID } from "./idTypes";
import { ListWrapper, Note } from "./types";

/** The minimal representation of a person*/
export interface PersonBase {
id: string;
id: PersonID;
firstName: string;
lastName: string;
}
Expand All @@ -28,7 +29,7 @@ export interface Expireable {

export interface PeopleGroup {
groupName: string;
id: number;
id: PeopleGroupID;
}

/** The representation of a person in the retrieve endpoint*/
Expand All @@ -44,7 +45,7 @@ export interface Person extends PersonSummary {
}

export interface Rental {
id: string;
id: GearItemID;
checkedout: string;
returned?: string;
totalAmount: number;
Expand All @@ -53,16 +54,10 @@ export interface Rental {
}

export interface GearToReturn {
id: string;
id: GearItemID;
daysCharged?: number;
}

export interface Affiliation {
id: string;
name: string;
dues: number;
}

export type CreatePersonArgs = {
firstName: string;
lastName: string;
Expand All @@ -73,51 +68,51 @@ async function createPerson(args: CreatePersonArgs): Promise<PersonSummary> {
return request(`/people/`, "POST", args);
}

async function addFFChecks(id: string, date: Date, checkNumber: string) {
async function addFFChecks(id: PersonID, date: Date, checkNumber: string) {
return request(`/people/${id}/frequent_flyer_check/`, "POST", {
expires: dayjs(date).format("YYYY-MM-DD"),
...(checkNumber && { checkNumber }),
});
}

async function addWaiver(id: string, date: Date) {
async function addWaiver(id: PersonID, date: Date) {
return request(`/people/${id}/waiver/`, "POST", {
expires: dayjs(date).format("YYYY-MM-DD"),
});
}

async function addMembership(id: string, date: Date, membershipType: string) {
async function addMembership(id: PersonID, date: Date, membershipType: string) {
return request(`/people/${id}/membership/`, "POST", {
expires: dayjs(date).format("YYYY-MM-DD"),
membershipType,
});
}

async function addNote(id: string, note: string) {
async function addNote(id: PersonID, note: string) {
return request(`/people/${id}/note/`, "POST", {
note,
});
}

async function archiveNote(personId: string, noteId: string) {
async function archiveNote(personId: PersonID, noteId: number) {
return request(`/people/${personId}/note/${noteId}/archive/`, "POST");
}

async function getPersonRentalHistory(
id: string,
id: PersonID,
page?: number,
): Promise<ListWrapper<Rental>> {
return request(`/people/${id}/rentals/`, "GET", { ...(page && { page }) });
}

async function checkoutGear(personID: string, gearIDs: string[]) {
async function checkoutGear(personID: PersonID, gearIDs: string[]) {
return request(`/people/${personID}/rentals/`, "POST", { gearIds: gearIDs });
}

async function returnGear(
personID: string,
personID: PersonID,
gear: GearToReturn[],
purchases: string[] = [],
purchases: number[] = [],
checkNumber: string = "",
useMitocCredit?: number,
) {
Expand All @@ -130,7 +125,7 @@ async function returnGear(
}

async function editPerson(
id: string,
id: PersonID,
firstName: string,
lastName: string,
email: string,
Expand All @@ -144,11 +139,11 @@ async function editPerson(
});
}

async function updatePersonGroups(id: string, groups: number[]) {
async function updatePersonGroups(id: PersonID, groups: number[]) {
return request(`/people/${id}/groups/`, "PUT", { groups });
}

async function addMitocCredit(id: string, amount: number) {
async function addMitocCredit(id: PersonID, amount: number) {
return request(`/people/${id}/credit/add/`, "PATCH", { amount });
}

Expand Down
11 changes: 6 additions & 5 deletions src/apiClient/types.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { NoteID, OfficeHourID, SignupID } from "./idTypes";
import { PeopleGroup, PersonBase, PersonWithOfficeAccess } from "./people";

export interface ListWrapper<T> {
Expand All @@ -17,11 +18,11 @@
export interface APIErrorType {
msg: string;
err: string;
args?: Record<string, any>;

Check warning on line 21 in src/apiClient/types.ts

View workflow job for this annotation

GitHub Actions / build (24.x)

Unexpected any. Specify a different type
}

export interface Note {
id: string;
id: NoteID;
note: string;
dateInserted: string;
author: PersonBase;
Expand All @@ -34,27 +35,27 @@
}

export interface OfficeHour {
googleId: string;
googleId: OfficeHourID;
title: string;
startTime: string;
endTime: string;
signups: {
id: string;
id: SignupID;
deskWorker: PersonWithOfficeAccess;
}[];
}

export interface PersonSignup {
id: SignupID;
creditRequested?: string;
approved?: string;
duration?: string;
date: string;
id: number;
note?: string;
eventType: string;
credit?: number;
}

export interface Signup extends PersonSignup {
deskWorker: { id: string; firstName: string; lastName: string };
deskWorker: PersonWithOfficeAccess;
}
4 changes: 3 additions & 1 deletion src/components/AddApprovalLink.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { Link } from "react-router-dom";

export function AddApprovalLink({ personId }: { personId?: string }) {
import { PersonID } from "src/apiClient/idTypes";

export function AddApprovalLink({ personId }: { personId?: PersonID }) {
const to =
personId == null ? "/add-approval" : `/add-approval?personId=${personId}`;
return (
Expand Down
3 changes: 2 additions & 1 deletion src/components/GearLink.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { LinkProps } from "react-router-dom";

import { GearItemID } from "src/apiClient/idTypes";
import { gearDbApi } from "src/redux/api";

import { PrefetchLink } from "./PrefetchLink";

type Props = { id: string } & Omit<LinkProps, "to">;
type Props = { id: GearItemID } & Omit<LinkProps, "to">;

export function GearLink({ id, ...otherProps }: Props) {
const prefetchGearItem = gearDbApi.usePrefetch("getGearItem");
Expand Down
5 changes: 3 additions & 2 deletions src/components/Notes.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import React, { useState } from "react";

import { NoteID } from "src/apiClient/idTypes";
import type { Person } from "src/apiClient/people";
import { ToggleExpandButton, ArchiveButton } from "src/components/Buttons";
import { ArchiveButton, ToggleExpandButton } from "src/components/Buttons";
import { TextArea } from "src/components/Inputs/TextArea";
import { formatDateTime } from "src/lib/fmtDate";

type Props = {
notes: Person["notes"];
onAdd: (note: string) => Promise<any>;
onArchive?: (id: string) => void;
onArchive?: (id: NoteID) => void;
};

export function Notes({ notes, onAdd, onArchive }: Props) {
Expand Down
Loading
Loading