module5
This commit is contained in:
123
web/src/api.ts
123
web/src/api.ts
@@ -164,6 +164,92 @@ export interface InquiryResult {
|
||||
created: { buyer: boolean; contact: boolean };
|
||||
}
|
||||
|
||||
// ------------------------------------------------- Notes / todos / Today
|
||||
export type TodoKind = 'TASK' | 'REVIEW';
|
||||
export type TodoStatus = 'OPEN' | 'DONE';
|
||||
|
||||
export interface Author {
|
||||
id: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
/** What a note or todo hangs off, with the ids needed to link to it. */
|
||||
export interface RefContext {
|
||||
type: 'buyer' | 'deal' | 'business' | 'nda';
|
||||
id: string;
|
||||
label: string;
|
||||
buyer_id: string | null;
|
||||
business_id: string | null;
|
||||
}
|
||||
|
||||
/** Exactly one of these for a note, at most one for a todo. */
|
||||
export interface Ref {
|
||||
buyer_id?: string;
|
||||
deal_id?: string;
|
||||
business_id?: string;
|
||||
nda_id?: string;
|
||||
}
|
||||
|
||||
export interface Note {
|
||||
id: string;
|
||||
text: string;
|
||||
highlight: boolean;
|
||||
created_at: string;
|
||||
author: Author | null;
|
||||
context: RefContext | null;
|
||||
}
|
||||
|
||||
export interface Todo {
|
||||
id: string;
|
||||
text: string;
|
||||
kind: TodoKind;
|
||||
status: TodoStatus;
|
||||
due_at: Day | null;
|
||||
document_id: string | null;
|
||||
document_name: string | null;
|
||||
done_at: string | null;
|
||||
done_by_name: string | null;
|
||||
assigned_to: Author;
|
||||
author: Author | null;
|
||||
context: RefContext | null;
|
||||
}
|
||||
|
||||
export interface FollowUp {
|
||||
id: string;
|
||||
status: DealStatus;
|
||||
follow_up_at: Day;
|
||||
buyer_id: string;
|
||||
buyer_name: string;
|
||||
business_id: string;
|
||||
business_name: string;
|
||||
created_by_id: string | null;
|
||||
created_by_name: string | null;
|
||||
}
|
||||
|
||||
export interface PendingNda {
|
||||
id: string;
|
||||
sent_at: Day;
|
||||
buyer_id: string;
|
||||
buyer_name: string;
|
||||
created_by_id: string | null;
|
||||
created_by_name: string | null;
|
||||
}
|
||||
|
||||
export interface TodayCounts {
|
||||
overdue_todos: number;
|
||||
due_todos: number;
|
||||
follow_ups: number;
|
||||
pending_ndas: number;
|
||||
total: number;
|
||||
}
|
||||
|
||||
export interface Today {
|
||||
todos: (Todo & { overdue: boolean })[];
|
||||
follow_ups: FollowUp[];
|
||||
pending_ndas: PendingNda[];
|
||||
counts: TodayCounts;
|
||||
}
|
||||
|
||||
export class ApiError extends Error {
|
||||
constructor(readonly status: number, message: string) {
|
||||
super(message);
|
||||
@@ -189,9 +275,12 @@ function send<T>(method: 'POST' | 'PATCH', path: string, body?: unknown): Promis
|
||||
|
||||
const post = <T>(path: string, body?: unknown) => send<T>('POST', path, body);
|
||||
const patch = <T>(path: string, body: unknown) => send<T>('PATCH', path, body);
|
||||
const del = <T>(path: string) => request<T>(path, { method: 'DELETE' });
|
||||
|
||||
const qs = (params: Record<string, string>) =>
|
||||
new URLSearchParams(params).toString();
|
||||
const qs = (params: Record<string, string | undefined>) =>
|
||||
new URLSearchParams(
|
||||
Object.entries(params).filter(([, value]) => value) as [string, string][],
|
||||
).toString();
|
||||
|
||||
export const api = {
|
||||
me: () => request<Staff>('/api/me'),
|
||||
@@ -233,6 +322,36 @@ export const api = {
|
||||
{ status, comment },
|
||||
),
|
||||
dealNotes: (id: string) => request<DealNote[]>(`/api/deals/${id}/notes`),
|
||||
|
||||
notes: (ref: Ref, includeRelated = false) =>
|
||||
request<Note[]>(
|
||||
`/api/notes?${qs({ ...ref, include_related: includeRelated ? 'true' : undefined })}`,
|
||||
),
|
||||
createNote: (ref: Ref, text: string, highlight: boolean) =>
|
||||
post<Note>('/api/notes', { ...ref, text, highlight }),
|
||||
updateNote: (id: string, body: { text?: string; highlight?: boolean }) =>
|
||||
patch<Note>(`/api/notes/${id}`, body),
|
||||
deleteNote: (id: string) => del<{ ok: boolean }>(`/api/notes/${id}`),
|
||||
|
||||
todos: (params: Ref & { assigned_to?: string; status?: TodoStatus }) =>
|
||||
request<Todo[]>(`/api/todos?${qs({ ...params })}`),
|
||||
createTodo: (body: Ref & Record<string, unknown>) => post<Todo>('/api/todos', body),
|
||||
updateTodo: (id: string, body: Record<string, unknown>) => patch<Todo>(`/api/todos/${id}`, body),
|
||||
todoDone: (id: string) => post<Todo>(`/api/todos/${id}/done`),
|
||||
todoReopen: (id: string) => post<Todo>(`/api/todos/${id}/reopen`),
|
||||
|
||||
today: (staffId?: string) => request<Today>(`/api/today?${qs({ staff_id: staffId })}`),
|
||||
followUpSent: (dealId: string, comment: string, rearm: boolean) =>
|
||||
post<{ id: string; status: DealStatus; follow_up_at: Day | null }>(
|
||||
`/api/deals/${dealId}/follow-up-sent`,
|
||||
{ comment, rearm },
|
||||
),
|
||||
/** Pins a business file so a REVIEW todo can point at it. */
|
||||
createDocument: (businessId: string, filePath: string) =>
|
||||
post<{ id: string; nas_path: string }>('/api/documents', {
|
||||
business_id: businessId,
|
||||
path: filePath,
|
||||
}),
|
||||
};
|
||||
|
||||
/** Same-origin streaming URL of one file inside a business directory. */
|
||||
|
||||
Reference in New Issue
Block a user