feat: bilingual support (FA/EN), IRANSans typography, RTL layout, and interactive product detail pages
This commit is contained in:
Vendored
+7
@@ -0,0 +1,7 @@
|
||||
import type { LiveClient } from "./core";
|
||||
|
||||
/**
|
||||
* Reads and writes outside React: the same reads as the hooks, as promises,
|
||||
* plus all writes. Use it in event handlers and plain modules.
|
||||
*/
|
||||
export declare const clickup: LiveClient;
|
||||
Vendored
+78
@@ -0,0 +1,78 @@
|
||||
import type { Delta, IsoDate, Ref, UseQueryOptions, UseQueryResult } from "./core";
|
||||
|
||||
export declare const COMMENT_TYPES: readonly ["message", "ai", "activity"];
|
||||
/**
|
||||
* `name` is a one-line preview of the body. The last ancestor is the task,
|
||||
* or for a reply the comment it replies to.
|
||||
*/
|
||||
export interface Comment extends Ref<"comment"> {
|
||||
ancestors: Array<Ref<"space" | "folder" | "list" | "task" | "comment">>;
|
||||
createdAt: IsoDate;
|
||||
updatedAt: IsoDate;
|
||||
type: (typeof COMMENT_TYPES)[number];
|
||||
/** The body; render it with `RichText`. */
|
||||
body: Delta;
|
||||
creator: Ref<"user">;
|
||||
assignee?: Ref<"user">;
|
||||
/** Set once the comment is resolved; there is no separate resolved flag. */
|
||||
resolvedBy?: Ref<"user">;
|
||||
/** One entry per emoji, with the workspace members who reacted. */
|
||||
reactions: Array<{
|
||||
emoji: string;
|
||||
users: Ref<"user">[];
|
||||
}>;
|
||||
}
|
||||
/**
|
||||
* `resolved` resolves or reopens the comment (read it back through
|
||||
* `resolvedBy`), `assignee: null` unassigns, and `myReactions` replaces the
|
||||
* viewer's reactions, leaving everyone else's.
|
||||
*/
|
||||
export interface CommentPatch {
|
||||
/** Replaces the body; read it back as `body`. */
|
||||
bodyMarkdown?: string;
|
||||
assignee?: Ref<"user"> | null;
|
||||
resolved?: boolean;
|
||||
myReactions?: string[];
|
||||
}
|
||||
/** `parent` is the task to comment on, or the comment to reply to. */
|
||||
export interface CreateCommentInput {
|
||||
parent: Ref<"task" | "comment">;
|
||||
/** The body, as markdown. */
|
||||
bodyMarkdown: string;
|
||||
assignee?: Ref<"user">;
|
||||
}
|
||||
export declare const COMMENT_ORDERS: readonly ["createdAt"];
|
||||
/**
|
||||
* Needs `parent` or `ancestor`. The other fields filter and sort on the
|
||||
* server before `limit` applies.
|
||||
* @example
|
||||
* useComments({ ancestor: task, resolved: false, assignee: "me" });
|
||||
*/
|
||||
export interface CommentSelector {
|
||||
/** A task, for its top-level comments; or a comment from `useComments`, for its replies. */
|
||||
parent?: Ref<"task" | "comment">;
|
||||
/** A task, for all its comments including replies. */
|
||||
ancestor?: Ref<"task"> | string;
|
||||
/** Defaults to 100, at most 500. */
|
||||
limit?: number;
|
||||
resolved?: boolean;
|
||||
/** `"me"` is the viewer. */
|
||||
assignee?: Ref<"user"> | "me";
|
||||
creator?: Ref<"user"> | "me";
|
||||
body?: {
|
||||
contains: string;
|
||||
};
|
||||
/** Oldest first when omitted; `direction` defaults to `"asc"`. */
|
||||
orderBy?: {
|
||||
field: (typeof COMMENT_ORDERS)[number];
|
||||
direction?: "asc" | "desc";
|
||||
};
|
||||
}
|
||||
export interface CommentsApi {
|
||||
list(selector: CommentSelector): Promise<Comment[]>;
|
||||
/** Comments on tasks; ids from chats or docs are left out. */
|
||||
get(targets: Array<Ref<"comment"> | string>): Promise<Comment[]>;
|
||||
create(input: CreateCommentInput): Promise<Ref<"comment">>;
|
||||
update(comment: Ref<"comment"> | string, patch: CommentPatch): Promise<void>;
|
||||
}
|
||||
export declare function useComments(selector: CommentSelector | Array<Ref<"comment"> | string>, options?: UseQueryOptions<Comment[]>): UseQueryResult<Comment[]>;
|
||||
Vendored
+72
@@ -0,0 +1,72 @@
|
||||
import type { Delta, Ref } from "./core";
|
||||
import type { CustomField } from "./customFields";
|
||||
import type { Status, Task } from "./tasks";
|
||||
import type { ReactElement } from "react";
|
||||
|
||||
export interface AvatarProps {
|
||||
/** A user or team, e.g. `task.creator` or one of `task.assignees`. */
|
||||
of: Ref<"user" | "team">;
|
||||
/** Diameter in px; 28 by default. */
|
||||
size?: number;
|
||||
className?: string;
|
||||
}
|
||||
/** A round avatar: the image, else initials for a user or a people glyph for a team. */
|
||||
export declare function Avatar({ of, size, className, }: AvatarProps): ReactElement;
|
||||
export interface AvatarGroupProps {
|
||||
/** Users and teams, e.g. `task.assignees`. */
|
||||
of: Array<Ref<"user" | "team">>;
|
||||
/** How many to show before the rest collapse into a `+N` bubble; 3 by default. */
|
||||
max?: number;
|
||||
/** Diameter of each avatar in px; 28 by default. */
|
||||
size?: number;
|
||||
className?: string;
|
||||
}
|
||||
/** Overlapping avatars with a `+N` bubble for the overflow; renders nothing for an empty list. */
|
||||
export declare function AvatarGroup({ of, max, size, className, }: AvatarGroupProps): ReactElement | null;
|
||||
export interface StatusBadgeProps {
|
||||
/** `task.status`, or one from `useStatuses`. */
|
||||
status: Status;
|
||||
className?: string;
|
||||
}
|
||||
/** The status name as ClickUp's filled, uppercase badge. Sized by the surrounding font; `className="text-xs"` makes it compact. */
|
||||
export declare function StatusBadge({ status, className, }: StatusBadgeProps): ReactElement;
|
||||
export interface PriorityFlagProps {
|
||||
/** `task.priority`; `undefined` renders nothing unless `showEmpty`. */
|
||||
priority: Task["priority"];
|
||||
/** Renders a slashed circle for `undefined`; off by default. */
|
||||
showEmpty?: boolean;
|
||||
className?: string;
|
||||
}
|
||||
/** ClickUp's priority flag, without a label. Sized by the surrounding font. */
|
||||
export declare function PriorityFlag({ priority, showEmpty, className, }: PriorityFlagProps): ReactElement | null;
|
||||
export interface TagProps {
|
||||
/** One of `task.tags`. */
|
||||
tag: Task["tags"][number];
|
||||
className?: string;
|
||||
}
|
||||
/** A tag as ClickUp's filled pill. Sized by the surrounding font. */
|
||||
export declare function Tag({ tag, className }: TagProps): ReactElement;
|
||||
export interface CustomFieldValueProps {
|
||||
/** One of `task.customFields`. */
|
||||
value: Task["customFields"][number];
|
||||
/** The field's definition from `useCustomFields`, matched by `id`; adds the option colors. */
|
||||
field?: CustomField;
|
||||
className?: string;
|
||||
}
|
||||
/**
|
||||
* A custom field value in the shape its type calls for: a check, a number, a
|
||||
* progress bar, a date, colored option pills, avatars for people, names for
|
||||
* tasks, links for attachments. Sized by the surrounding font.
|
||||
*/
|
||||
export declare function CustomFieldValue({ value, field, className, }: CustomFieldValueProps): ReactElement;
|
||||
export interface RichTextProps {
|
||||
/** A task or comment `body`. */
|
||||
value: Delta;
|
||||
className?: string;
|
||||
}
|
||||
/**
|
||||
* A body as ClickUp shows it, rendered by the host: mentions, tables, embeds
|
||||
* and the rest. Empty while the HTML loads; plain text if the host cannot
|
||||
* render it.
|
||||
*/
|
||||
export declare function RichText({ value, className }: RichTextProps): ReactElement;
|
||||
Vendored
+155
@@ -0,0 +1,155 @@
|
||||
import type { CommentsApi } from "./comments";
|
||||
import type { CustomFieldsApi } from "./customFields";
|
||||
import type { FoldersApi, ListsApi, SpacesApi } from "./hierarchy";
|
||||
import type { InboxApi } from "./inbox";
|
||||
import type { TeamsApi, User, UsersApi } from "./people";
|
||||
import type { RemindersApi } from "./reminders";
|
||||
import type { SearchHit, SearchQuery } from "./search";
|
||||
import type { TasksApi } from "./tasks";
|
||||
|
||||
/**
|
||||
* An ISO 8601 string as `Date#toISOString()` writes it; a plain `YYYY-MM-DD`
|
||||
* is accepted as input and means that day in the viewer's timezone. A
|
||||
* required date the host has no value for reads as the Unix epoch,
|
||||
* `1970-01-01T00:00:00.000Z`.
|
||||
*/
|
||||
export type IsoDate = string;
|
||||
/** Rich text as a Quill delta, plain JSON ops; render it with `RichText`. */
|
||||
export interface Delta {
|
||||
ops: ReadonlyArray<Record<string, unknown>>;
|
||||
}
|
||||
export declare const ASSET_KINDS: readonly ["space", "folder", "list", "status", "customField", "task", "taskType", "comment", "user", "team", "inboxBundle", "reminder"];
|
||||
export type AssetKind = (typeof ASSET_KINDS)[number];
|
||||
/**
|
||||
* A pointer to an asset; every full asset (a `Task`, a `List`, …) is also a
|
||||
* `Ref`, so anything a hook returns can be passed where a ref is expected.
|
||||
* Where a parameter is `Ref | string`, the string is the asset's id.
|
||||
*/
|
||||
export interface Ref<K extends AssetKind = AssetKind> {
|
||||
kind: K;
|
||||
id: string;
|
||||
/** Empty when the viewer cannot see the asset, e.g. a related task in a private list. */
|
||||
name: string;
|
||||
/**
|
||||
* Link to the asset in ClickUp; empty for statuses, custom fields and
|
||||
* reminders. Users and teams link to the workspace's People page.
|
||||
*/
|
||||
url: string;
|
||||
/** Avatar or thumbnail, where the asset has one. */
|
||||
image?: string;
|
||||
}
|
||||
/** `pending` until the first result or failure. */
|
||||
export type QueryStatus = "pending" | "error" | "success";
|
||||
/** TanStack Query v5's `useQuery` result. */
|
||||
export interface UseQueryResult<T> {
|
||||
/** Undefined until the first result; kept while refetching. */
|
||||
data: T | undefined;
|
||||
error: LiveError | null;
|
||||
status: QueryStatus;
|
||||
isPending: boolean;
|
||||
/** Pending and fetching: the first result is on its way. */
|
||||
isLoading: boolean;
|
||||
/** True while a request is in flight, including the refresh behind a cached result. */
|
||||
isFetching: boolean;
|
||||
/** Fetching with data already showing. */
|
||||
isRefetching: boolean;
|
||||
/** True while `data` is the option's `placeholderData`, not a result. */
|
||||
isPlaceholderData: boolean;
|
||||
isError: boolean;
|
||||
isSuccess: boolean;
|
||||
/** When `data` was read from ClickUp, as a timestamp; 0 without data. */
|
||||
dataUpdatedAt: number;
|
||||
/** Reloads from ClickUp. Rarely needed, since writes refetch what they change. */
|
||||
refetch: () => Promise<void>;
|
||||
}
|
||||
export interface UseQueryOptions<T = unknown> {
|
||||
/** Pass false to skip fetching, e.g. while an input is still unknown. */
|
||||
enabled?: boolean;
|
||||
/** Shown as `data` until the first result, e.g. `keepPreviousData` to keep the last rows while a changed selector loads. */
|
||||
placeholderData?: T | ((previous: T | undefined) => T | undefined);
|
||||
/** Milliseconds between refetches while the tab is visible; never under 10 000. Rarely needed: writes refetch, and so does returning to the tab. */
|
||||
refetchInterval?: number;
|
||||
/** Pass false to wait for fresh data instead of painting the viewer's last result first. Rarely needed: the data refreshes anyway. */
|
||||
cache?: boolean;
|
||||
}
|
||||
/** A `placeholderData` that keeps the previous selector's rows on screen while the new ones load. */
|
||||
export declare function keepPreviousData<T>(previous: T | undefined): T | undefined;
|
||||
/**
|
||||
* - `denied`: the viewer declined the approval in ClickUp.
|
||||
* - `forbidden`: the host or viewer is not allowed to; hide or disable the feature.
|
||||
* - `invalid`: the call itself is wrong (selector, id or input); fix the code.
|
||||
* - `failed`: something went wrong; retry when `retryable`.
|
||||
*/
|
||||
export type LiveErrorCode = "denied" | "forbidden" | "invalid" | "failed";
|
||||
export declare class LiveError extends Error {
|
||||
readonly code: LiveErrorCode;
|
||||
/** True for rate limits, timeouts and other transient errors. */
|
||||
readonly retryable: boolean;
|
||||
/** The host's raw reason, for debugging. */
|
||||
readonly reason?: string;
|
||||
constructor(code: LiveErrorCode, message: string, options?: {
|
||||
retryable?: boolean;
|
||||
reason?: string;
|
||||
});
|
||||
}
|
||||
/**
|
||||
* The same reads as the hooks, as promises, plus all writes. Each changed
|
||||
* field needs the viewer's approval in ClickUp; a write stops at the first
|
||||
* denial.
|
||||
*/
|
||||
export interface LiveClient {
|
||||
spaces: SpacesApi;
|
||||
folders: FoldersApi;
|
||||
lists: ListsApi;
|
||||
customFields: CustomFieldsApi;
|
||||
tasks: TasksApi;
|
||||
comments: CommentsApi;
|
||||
users: UsersApi;
|
||||
teams: TeamsApi;
|
||||
inbox: InboxApi;
|
||||
reminders: RemindersApi;
|
||||
/** Opens a ClickUp url in the app. */
|
||||
navigate(url: string): Promise<void>;
|
||||
/** ClickUp's own search, run as the viewer. */
|
||||
search(query: SearchQuery): Promise<SearchHit[]>;
|
||||
/** Sends a prompt to Brain, ClickUp's AI, on the user's behalf. */
|
||||
askBrain(prompt: string): Promise<void>;
|
||||
/** A task or comment `body` as the HTML ClickUp shows for it; `RichText` renders this. */
|
||||
toHtml(body: Delta): Promise<string>;
|
||||
/** The user viewing the artifact, known to the host up front: no read, no loading state. */
|
||||
readonly viewer: User;
|
||||
}
|
||||
export type MutationStatus = "idle" | "pending" | "success" | "error";
|
||||
export type MutationFunction<TData, TVariables> = (variables: TVariables) => Promise<TData>;
|
||||
export interface MutationOptions<TData, TVariables> {
|
||||
onSuccess?: (data: TData, variables: TVariables) => void;
|
||||
onError?: (error: Error, variables: TVariables) => void;
|
||||
onSettled?: (data: TData | undefined, error: Error | null, variables: TVariables) => void;
|
||||
}
|
||||
export interface UseMutationOptions<TData, TVariables> extends MutationOptions<TData, TVariables> {
|
||||
mutationFn: MutationFunction<TData, TVariables>;
|
||||
}
|
||||
export interface UseMutationResult<TData, TVariables> {
|
||||
data: TData | undefined;
|
||||
error: Error | null;
|
||||
/** What the last `mutate` was called with; undefined while idle. */
|
||||
variables: TVariables | undefined;
|
||||
isError: boolean;
|
||||
isPending: boolean;
|
||||
isSuccess: boolean;
|
||||
status: MutationStatus;
|
||||
/** Never throws; failures show up in `error`. */
|
||||
mutate: (variables: TVariables, options?: MutationOptions<TData, TVariables>) => void;
|
||||
mutateAsync: (variables: TVariables, options?: MutationOptions<TData, TVariables>) => Promise<TData>;
|
||||
reset: () => void;
|
||||
}
|
||||
/**
|
||||
* TanStack Query v5's `useMutation`; a bare function is taken as
|
||||
* `mutationFn`. Call `mutate` from an event handler and leave the rest to
|
||||
* the kit: on-screen queries update optimistically, roll back if the viewer
|
||||
* declines, and refetch on success.
|
||||
* @example
|
||||
* const complete = useMutation({ mutationFn: (status: Status) => clickup.tasks.update(task, { status }) });
|
||||
* <button disabled={complete.isPending} onClick={() => complete.mutate(done)}>Mark done</button>
|
||||
*/
|
||||
export declare function useMutation<TData, TVariables = void>(options: UseMutationOptions<TData, TVariables> | MutationFunction<TData, TVariables>): UseMutationResult<TData, TVariables>;
|
||||
Vendored
+22
@@ -0,0 +1,22 @@
|
||||
import type { Ref, UseQueryOptions, UseQueryResult } from "./core";
|
||||
|
||||
export declare const CUSTOM_FIELD_TYPES: readonly ["short_text", "text", "email", "url", "phone", "number", "currency", "checkbox", "date", "drop_down", "labels", "users", "tasks", "list_relationship", "emoji", "votes", "manual_progress", "automatic_progress", "attachment", "location", "formula", "signature", "button"];
|
||||
/** A custom field's definition. Values live on each task in `customFields`, matched by `id`. */
|
||||
export interface CustomField extends Ref<"customField"> {
|
||||
/** Ends at the space, folder or list the field is configured on. */
|
||||
ancestors: Array<Ref<"space" | "folder" | "list">>;
|
||||
type: (typeof CUSTOM_FIELD_TYPES)[number];
|
||||
/** For drop-down and labels fields: the allowed values, in display order. */
|
||||
options?: Array<{
|
||||
name: string;
|
||||
color: string;
|
||||
}>;
|
||||
}
|
||||
/** The fields available on a task, or on tasks in a space, folder or list. Inherited fields are included; `ancestors` shows where each is configured. */
|
||||
export interface CustomFieldSelector {
|
||||
parent: Ref<"task" | "space" | "folder" | "list">;
|
||||
}
|
||||
export interface CustomFieldsApi {
|
||||
list(selector: CustomFieldSelector): Promise<CustomField[]>;
|
||||
}
|
||||
export declare function useCustomFields(selector: CustomFieldSelector, options?: UseQueryOptions<CustomField[]>): UseQueryResult<CustomField[]>;
|
||||
Vendored
+113
@@ -0,0 +1,113 @@
|
||||
import type { Ref, UseMutationResult, UseQueryOptions, UseQueryResult } from "./core";
|
||||
import type { CustomField } from "./customFields";
|
||||
import type { Status, Task } from "./tasks";
|
||||
|
||||
/**
|
||||
* Typed records over a schema the `solution` CLI provisioned: a record is one
|
||||
* task in the entity's List, its custom fields named by the schema's logical
|
||||
* keys. Reads are shaped like TanStack Query's `useQuery`, writes like
|
||||
* `useMutation`, and every write waits for the viewer's approval.
|
||||
*/
|
||||
export declare function defineEntity<R extends RecordBase, I extends object, P extends object, S extends string>(schema: EntitySchema): Entity<R, I, P, S>;
|
||||
/**
|
||||
* Tasks by id, in the order given. An empty list is a settled `[]` with no
|
||||
* loading state, so a relationship hook fed an unlinked record renders its
|
||||
* empty state at once.
|
||||
*/
|
||||
export declare function useTasksByIds(ids: readonly string[], options?: UseQueryOptions<Task[]>): UseQueryResult<Task[]>;
|
||||
|
||||
/** A property every task has, mapped to a record property by the schema. */
|
||||
type NativeProperty = "name" | "status" | "assignees" | "dueDate" | "startDate" | "priority" | "description";
|
||||
interface OptionSpec {
|
||||
id: string;
|
||||
/** The option's name in ClickUp; what the kit reads and writes. */
|
||||
label: string;
|
||||
}
|
||||
interface FieldSpec {
|
||||
/** The record property; a relationship becomes <key>Ids and <key>Refs. */
|
||||
key: string;
|
||||
/** The ClickUp custom field id. */
|
||||
id: string;
|
||||
name: string;
|
||||
type: CustomField["type"];
|
||||
required: boolean;
|
||||
/** False keeps a type the kit cannot read off the record and out of the drift check. */
|
||||
readable: boolean;
|
||||
/** The kit cannot write this type yet; writes are rejected. */
|
||||
readOnly: boolean;
|
||||
/** Drop-down and labels only: logical option key to ClickUp option. */
|
||||
options?: Readonly<Record<string, OptionSpec>>;
|
||||
targetEntity?: string;
|
||||
targetListId?: string;
|
||||
}
|
||||
/** One entity's frozen ids, as _schema.ts exports them. */
|
||||
export interface EntitySchema {
|
||||
key: string;
|
||||
list: {
|
||||
id: string;
|
||||
name: string;
|
||||
url: string;
|
||||
};
|
||||
taskType?: {
|
||||
id: number;
|
||||
name: string;
|
||||
};
|
||||
natives: ReadonlyArray<{
|
||||
key: string;
|
||||
native: NativeProperty;
|
||||
}>;
|
||||
fields: readonly FieldSpec[];
|
||||
}
|
||||
export interface RecordBase {
|
||||
id: string;
|
||||
/** Link to the task in ClickUp; safe to render for the viewer. */
|
||||
url: string;
|
||||
/** The underlying clickup-kit Task, for anything the schema does not model. */
|
||||
task: Task;
|
||||
}
|
||||
/**
|
||||
* status, statusType, limit and includeClosed are applied by ClickUp; the
|
||||
* entity's task type, where and orderBy run in the browser on the decoded
|
||||
* records. With ids, ClickUp returns exactly those tasks and every filter
|
||||
* runs in the browser.
|
||||
*/
|
||||
export interface ListFilter<R, S extends string> {
|
||||
/** Status names as shown in ClickUp, case-insensitive. */
|
||||
status?: readonly S[];
|
||||
statusType?: ReadonlyArray<Status["type"]>;
|
||||
ids?: readonly string[];
|
||||
limit?: number;
|
||||
/** ClickUp hides tasks in the closing status unless this is true. */
|
||||
includeClosed?: boolean;
|
||||
where?: (record: R) => boolean;
|
||||
/** Unset values sort last. */
|
||||
orderBy?: {
|
||||
field: keyof R;
|
||||
direction?: "asc" | "desc";
|
||||
};
|
||||
}
|
||||
interface EntityClient<R, I, P, S extends string> {
|
||||
list(filter?: ListFilter<R, S>): Promise<R[]>;
|
||||
get(id: string): Promise<R>;
|
||||
/** Resolves with the new task's ref when the host reports it. */
|
||||
create(input: I): Promise<Ref<"task"> | undefined>;
|
||||
/** undefined leaves a property alone; null clears one that is optional. */
|
||||
update(id: string, patch: P): Promise<void>;
|
||||
/** Moves the task to the List's closing status. There is no delete. */
|
||||
close(id: string): Promise<void>;
|
||||
}
|
||||
export interface Entity<R extends RecordBase, I, P, S extends string> {
|
||||
schema: EntitySchema;
|
||||
decode(task: Task): R;
|
||||
client: EntityClient<R, I, P, S>;
|
||||
useList(filter?: ListFilter<R, S>, options?: UseQueryOptions<Task[]>): UseQueryResult<R[]>;
|
||||
/** data is undefined while loading and when no task has that id; isSuccess tells them apart. */
|
||||
useOne(id: string | undefined, options?: UseQueryOptions<Task[]>): UseQueryResult<R | undefined>;
|
||||
useMany(ids: readonly string[], options?: UseQueryOptions<Task[]>): UseQueryResult<R[]>;
|
||||
useCreate(): UseMutationResult<Ref<"task"> | undefined, I>;
|
||||
useUpdate(): UseMutationResult<void, {
|
||||
id: string;
|
||||
patch: P;
|
||||
}>;
|
||||
useClose(): UseMutationResult<void, string>;
|
||||
}
|
||||
Vendored
+43
@@ -0,0 +1,43 @@
|
||||
import type { Ref, UseQueryOptions, UseQueryResult } from "./core";
|
||||
|
||||
export interface Space extends Ref<"space"> {
|
||||
private: boolean;
|
||||
}
|
||||
export interface Folder extends Ref<"folder"> {
|
||||
ancestors: Array<Ref<"space" | "folder">>;
|
||||
private: boolean;
|
||||
}
|
||||
export interface List extends Ref<"list"> {
|
||||
ancestors: Array<Ref<"space" | "folder">>;
|
||||
private: boolean;
|
||||
}
|
||||
/** Needs `parent` or `ancestor`. */
|
||||
export interface FolderSelector {
|
||||
parent?: Ref<"space" | "folder">;
|
||||
ancestor?: Ref<"space"> | string;
|
||||
/** Defaults to 100, at most 500. */
|
||||
limit?: number;
|
||||
}
|
||||
/** Needs `parent` or `ancestor`. */
|
||||
export interface ListSelector {
|
||||
parent?: Ref<"space" | "folder">;
|
||||
ancestor?: Ref<"space"> | string;
|
||||
/** Defaults to 100, at most 500. */
|
||||
limit?: number;
|
||||
}
|
||||
export interface SpacesApi {
|
||||
list(): Promise<Space[]>;
|
||||
get(targets: Array<Ref<"space"> | string>): Promise<Space[]>;
|
||||
}
|
||||
export interface FoldersApi {
|
||||
list(selector: FolderSelector): Promise<Folder[]>;
|
||||
get(targets: Array<Ref<"folder"> | string>): Promise<Folder[]>;
|
||||
}
|
||||
export interface ListsApi {
|
||||
list(selector: ListSelector): Promise<List[]>;
|
||||
get(targets: Array<Ref<"list"> | string>): Promise<List[]>;
|
||||
}
|
||||
/** Every space, or exactly the given ones. */
|
||||
export declare function useSpaces(targets?: Array<Ref<"space"> | string>, options?: UseQueryOptions<Space[]>): UseQueryResult<Space[]>;
|
||||
export declare function useFolders(selector: FolderSelector | Array<Ref<"folder"> | string>, options?: UseQueryOptions<Folder[]>): UseQueryResult<Folder[]>;
|
||||
export declare function useLists(selector: ListSelector | Array<Ref<"list"> | string>, options?: UseQueryOptions<List[]>): UseQueryResult<List[]>;
|
||||
Vendored
+55
@@ -0,0 +1,55 @@
|
||||
import type { IsoDate, Ref, UseQueryOptions, UseQueryResult } from "./core";
|
||||
|
||||
/** `primary` holds messages and mentions, `other` everything else, `reminder` reminders. */
|
||||
export declare const INBOX_CATEGORIES: readonly ["primary", "other", "reminder"];
|
||||
export declare const INBOX_STATES: readonly ["active", "snoozed", "cleared"];
|
||||
export declare const NOTIFICATION_TYPES: readonly ["comment", "mention", "assignment", "status", "reminder", "other"];
|
||||
/**
|
||||
* One inbox row: the notifications about one asset, whose name and url the
|
||||
* bundle mirrors. For a reminder, `related` is the reminder itself.
|
||||
*/
|
||||
export interface InboxBundle extends Ref<"inboxBundle"> {
|
||||
category: (typeof INBOX_CATEGORIES)[number];
|
||||
state: (typeof INBOX_STATES)[number];
|
||||
unreadCount: number;
|
||||
mentionsMe: boolean;
|
||||
assignedToMe: boolean;
|
||||
/** When a snoozed bundle comes back; for a reminder, when it is due. */
|
||||
snoozedUntil?: IsoDate;
|
||||
clearedAt?: IsoDate;
|
||||
/** The asset the bundle is about. */
|
||||
related?: Ref;
|
||||
/** Newest first. */
|
||||
notifications: Array<{
|
||||
type: (typeof NOTIFICATION_TYPES)[number];
|
||||
at: IsoDate;
|
||||
/** Who acted; absent for automations. */
|
||||
actor?: Ref<"user">;
|
||||
/** What the notification points at, e.g. the comment itself. */
|
||||
related?: Ref;
|
||||
}>;
|
||||
}
|
||||
/**
|
||||
* `state: "cleared"` clears, `"snoozed"` snoozes until `snoozedUntil`,
|
||||
* `"active"` brings a cleared or snoozed bundle back. `read` marks it read or
|
||||
* unread; read the state back through `unreadCount`.
|
||||
*/
|
||||
export interface InboxBundlePatch {
|
||||
state?: InboxBundle["state"];
|
||||
snoozedUntil?: IsoDate;
|
||||
read?: boolean;
|
||||
}
|
||||
export interface InboxSelector {
|
||||
/** Defaults to `"active"`. */
|
||||
state?: InboxBundle["state"];
|
||||
category?: InboxBundle["category"];
|
||||
/** Defaults to 100, at most 500. */
|
||||
limit?: number;
|
||||
}
|
||||
export interface InboxApi {
|
||||
list(selector?: InboxSelector): Promise<InboxBundle[]>;
|
||||
/** Any state, cleared included. */
|
||||
get(targets: Array<Ref<"inboxBundle"> | string>): Promise<InboxBundle[]>;
|
||||
update(bundle: Ref<"inboxBundle"> | string, patch: InboxBundlePatch): Promise<void>;
|
||||
}
|
||||
export declare function useInbox(selector?: InboxSelector | Array<Ref<"inboxBundle"> | string>, options?: UseQueryOptions<InboxBundle[]>): UseQueryResult<InboxBundle[]>;
|
||||
Vendored
+19
@@ -0,0 +1,19 @@
|
||||
import type { Ref, UseQueryOptions, UseQueryResult } from "./core";
|
||||
|
||||
/** A workspace member; `image` is the avatar. */
|
||||
export type User = Ref<"user">;
|
||||
export interface Team extends Ref<"team"> {
|
||||
members: Array<Ref<"user">>;
|
||||
}
|
||||
export interface UsersApi {
|
||||
list(): Promise<User[]>;
|
||||
get(targets: Array<Ref<"user"> | string>): Promise<User[]>;
|
||||
}
|
||||
export interface TeamsApi {
|
||||
list(): Promise<Team[]>;
|
||||
get(targets: Array<Ref<"team"> | string>): Promise<Team[]>;
|
||||
}
|
||||
/** Every member, or exactly the given ones. */
|
||||
export declare function useUsers(targets?: Array<Ref<"user"> | string>, options?: UseQueryOptions<User[]>): UseQueryResult<User[]>;
|
||||
/** Every team, or exactly the given ones. */
|
||||
export declare function useTeams(targets?: Array<Ref<"team"> | string>, options?: UseQueryOptions<Team[]>): UseQueryResult<Team[]>;
|
||||
Vendored
+31
@@ -0,0 +1,31 @@
|
||||
import type { IsoDate, Ref, UseQueryOptions, UseQueryResult } from "./core";
|
||||
|
||||
export interface Reminder extends Ref<"reminder"> {
|
||||
dueAt?: IsoDate;
|
||||
/** Set once completed; there is no separate flag. */
|
||||
completedAt?: IsoDate;
|
||||
related?: Ref<"task">;
|
||||
}
|
||||
/** `completed: true` cannot be undone. */
|
||||
export interface ReminderPatch {
|
||||
dueAt?: IsoDate;
|
||||
completed?: true;
|
||||
}
|
||||
export interface CreateReminderInput {
|
||||
name: string;
|
||||
dueAt?: IsoDate;
|
||||
related?: Ref<"task"> | string;
|
||||
}
|
||||
export interface ReminderSelector {
|
||||
includeCompleted?: boolean;
|
||||
/** Defaults to 100, at most 500. */
|
||||
limit?: number;
|
||||
}
|
||||
export interface RemindersApi {
|
||||
list(selector?: ReminderSelector): Promise<Reminder[]>;
|
||||
/** Completed ones included. */
|
||||
get(targets: Array<Ref<"reminder"> | string>): Promise<Reminder[]>;
|
||||
create(input: CreateReminderInput): Promise<Ref<"reminder">>;
|
||||
update(reminder: Ref<"reminder"> | string, patch: ReminderPatch): Promise<void>;
|
||||
}
|
||||
export declare function useReminders(selector?: ReminderSelector | Array<Ref<"reminder"> | string>, options?: UseQueryOptions<Reminder[]>): UseQueryResult<Reminder[]>;
|
||||
Vendored
+29
@@ -0,0 +1,29 @@
|
||||
import type { IsoDate, Ref, UseQueryOptions, UseQueryResult } from "./core";
|
||||
|
||||
export declare const SEARCH_KINDS: readonly ["task", "list", "folder", "space", "comment"];
|
||||
export declare const SEARCH_ORDERS: readonly ["createdAt", "updatedAt", "dueAt"];
|
||||
/** A ref plus why and when it matched. For the full asset, load it through its kind, e.g. `useTasks([hit.id])`. */
|
||||
export interface SearchHit extends Ref<(typeof SEARCH_KINDS)[number]> {
|
||||
/** A plain-text preview; the passage that matched when `keywords` were given. */
|
||||
snippet?: string;
|
||||
createdAt: IsoDate;
|
||||
updatedAt: IsoDate;
|
||||
/** When the viewer last opened it. */
|
||||
viewedAt?: IsoDate;
|
||||
}
|
||||
export interface SearchQuery {
|
||||
/** Matched against names, descriptions and comments. Omit for a plain listing, ordered by `orderBy`. */
|
||||
keywords?: string;
|
||||
/** Every searchable kind by default. */
|
||||
kinds?: Array<SearchHit["kind"]>;
|
||||
ancestor?: Ref<"space" | "folder" | "list">;
|
||||
/** Relevance when omitted; `direction` defaults to `"desc"`. */
|
||||
orderBy?: {
|
||||
field: (typeof SEARCH_ORDERS)[number];
|
||||
direction?: "asc" | "desc";
|
||||
};
|
||||
/** Defaults to 25, at most 50. */
|
||||
limit?: number;
|
||||
}
|
||||
/** ClickUp's own search, run as the viewer; hits come in ClickUp's order. */
|
||||
export declare function useSearch(query: SearchQuery, options?: UseQueryOptions<SearchHit[]>): UseQueryResult<SearchHit[]>;
|
||||
Vendored
+169
@@ -0,0 +1,169 @@
|
||||
import type { Delta, IsoDate, Ref, UseQueryOptions, UseQueryResult } from "./core";
|
||||
import type { CustomField } from "./customFields";
|
||||
|
||||
export declare const PRIORITIES: readonly ["urgent", "high", "normal", "low"];
|
||||
/** `open` is not started, `active` in progress, `done` finished, and `closed` the list's final status. */
|
||||
export declare const STATUS_TYPES: readonly ["open", "active", "done", "closed"];
|
||||
export interface Status extends Ref<"status"> {
|
||||
type: (typeof STATUS_TYPES)[number];
|
||||
color: string;
|
||||
}
|
||||
/** The effective statuses of a list, folder or space: what a task created there uses, in board order. */
|
||||
export interface StatusSelector {
|
||||
/** A bare string is a list id. */
|
||||
parent: Ref<"space" | "folder" | "list"> | string;
|
||||
}
|
||||
export interface Task extends Ref<"task"> {
|
||||
/** The space, folder and home list, then the parent task if this is a subtask. */
|
||||
ancestors: Array<Ref<"space" | "folder" | "list" | "task">>;
|
||||
/** Other lists the task appears in, besides its home list. */
|
||||
otherLists: Array<Ref<"list">>;
|
||||
private: boolean;
|
||||
createdAt: IsoDate;
|
||||
updatedAt: IsoDate;
|
||||
startAt?: IsoDate;
|
||||
dueAt?: IsoDate;
|
||||
status: Status;
|
||||
type: TaskType;
|
||||
priority?: (typeof PRIORITIES)[number];
|
||||
/** Sprint points. */
|
||||
points?: number;
|
||||
tags: Array<{
|
||||
name: string;
|
||||
color: string;
|
||||
}>;
|
||||
/** Set fields only; definitions are in `useCustomFields`, matched by `id`. `value` follows `type`: dates are `IsoDate`, drop-downs and labels option names, attachments urls, users and tasks refs. */
|
||||
customFields: Array<{
|
||||
id: string;
|
||||
name: string;
|
||||
type: CustomField["type"];
|
||||
value: string | number | boolean | string[] | Ref<"user" | "team" | "task">[];
|
||||
}>;
|
||||
/** The description; render it with `RichText`. */
|
||||
body: Delta;
|
||||
creator: Ref<"user">;
|
||||
assignees: Array<Ref<"user" | "team">>;
|
||||
watchers: Array<Ref<"user">>;
|
||||
checklists: ChecklistItem[];
|
||||
}
|
||||
/** A task's checklists are headings; the items nested below them are checkable, at any depth. */
|
||||
export interface ChecklistItem {
|
||||
/** Empty for an entry that does not exist yet. */
|
||||
id: string;
|
||||
name: string;
|
||||
/** Absent on headings. */
|
||||
done?: boolean;
|
||||
items: ChecklistItem[];
|
||||
}
|
||||
/**
|
||||
* The fields to change: `undefined` leaves one alone, `null` clears it.
|
||||
* Collections replace the whole list, so read-modify-write them; checklists
|
||||
* are diffed by `id`, so keep existing ids and give new entries an empty one.
|
||||
* @example
|
||||
* await clickup.tasks.update(task, { dueAt: null, assignees: [...task.assignees, user] });
|
||||
*/
|
||||
export interface TaskPatch {
|
||||
name?: string;
|
||||
/** Replaces the description; read it back as `body`. */
|
||||
bodyMarkdown?: string;
|
||||
/** One of the home list's statuses, from `useStatuses`. */
|
||||
status?: Ref<"status">;
|
||||
type?: Ref<"taskType"> | string;
|
||||
priority?: Task["priority"] | null;
|
||||
points?: number | null;
|
||||
startAt?: IsoDate | null;
|
||||
dueAt?: IsoDate | null;
|
||||
/** Makes the task a subtask of `parent`. */
|
||||
parent?: Ref<"task"> | string;
|
||||
assignees?: Task["assignees"];
|
||||
/** Only the viewer can be added or removed. */
|
||||
watchers?: Task["watchers"];
|
||||
tags?: Task["tags"];
|
||||
/** Users, tasks and attachments fields cannot be written yet and reject with `forbidden`. */
|
||||
customFields?: Task["customFields"];
|
||||
checklists?: Task["checklists"];
|
||||
}
|
||||
/** `list` is where the task goes; `parent` makes it a subtask of that task. */
|
||||
export interface CreateTaskInput {
|
||||
list: Ref<"list"> | string;
|
||||
parent?: Ref<"task"> | string;
|
||||
name: string;
|
||||
/** The description, as markdown. */
|
||||
bodyMarkdown?: string;
|
||||
status?: Ref<"status">;
|
||||
type?: Ref<"taskType"> | string;
|
||||
priority?: Task["priority"];
|
||||
dueAt?: IsoDate;
|
||||
startAt?: IsoDate;
|
||||
assignees?: Task["assignees"];
|
||||
}
|
||||
/** Relative days, in the viewer's timezone. */
|
||||
export declare const DATE_PRESETS: readonly ["today", "tomorrow", "next7Days"];
|
||||
/** Inclusive day bounds; give one or both. */
|
||||
export interface DateRange {
|
||||
from?: IsoDate;
|
||||
to?: IsoDate;
|
||||
}
|
||||
export type DateFilter = (typeof DATE_PRESETS)[number] | DateRange;
|
||||
export declare const TASK_ORDERS: readonly ["name", "priority", "status", "startAt", "dueAt", "createdAt", "updatedAt"];
|
||||
/**
|
||||
* Needs `parent` or `ancestor`. The other fields filter and sort on the
|
||||
* server before `limit` applies.
|
||||
* @example
|
||||
* useTasks({ ancestor: space, assignees: ["me"], dueAt: "overdue" });
|
||||
* useTasks({ parent: list, status: { type: ["active"] }, orderBy: { field: "priority" } });
|
||||
*/
|
||||
export interface TaskSelector {
|
||||
parent?: Ref<"list"> | string;
|
||||
ancestor?: Ref<"space" | "folder" | "list">;
|
||||
/** Defaults to 100, at most 500. */
|
||||
limit?: number;
|
||||
/** Defaults to false. Hides tasks in the list's final status only; `done` tasks still come. */
|
||||
includeClosed?: boolean;
|
||||
/** Statuses from `useStatuses`, or every status of these types. */
|
||||
status?: Ref<"status">[] | {
|
||||
type: Status["type"][];
|
||||
};
|
||||
/** Users and teams, `"me"` for the viewer; or `"none"` for unassigned tasks, `"any"` for assigned ones. */
|
||||
assignees?: Array<Ref<"user" | "team"> | "me"> | "none" | "any";
|
||||
priority?: Array<NonNullable<Task["priority"]>>;
|
||||
tags?: string[];
|
||||
/** Tasks whose name contains this text. Descriptions and comments are `useSearch`'s job. */
|
||||
name?: {
|
||||
contains: string;
|
||||
};
|
||||
startAt?: DateFilter | "none" | "any";
|
||||
dueAt?: DateFilter | "none" | "any" | "overdue";
|
||||
createdAt?: DateFilter;
|
||||
updatedAt?: DateFilter;
|
||||
/** The host's own order when omitted; `direction` defaults to `"asc"`. */
|
||||
orderBy?: {
|
||||
field: (typeof TASK_ORDERS)[number];
|
||||
direction?: "asc" | "desc";
|
||||
};
|
||||
}
|
||||
/** One of the workspace's task types, e.g. Task, Milestone, Bug. */
|
||||
export interface TaskType extends Ref<"taskType"> {
|
||||
description?: string;
|
||||
}
|
||||
export interface TasksApi {
|
||||
list(selector: TaskSelector): Promise<Task[]>;
|
||||
get(targets: Array<Ref<"task"> | string>): Promise<Task[]>;
|
||||
create(input: CreateTaskInput): Promise<Ref<"task">>;
|
||||
update(task: Ref<"task"> | string, patch: TaskPatch): Promise<void>;
|
||||
/**
|
||||
* One read-only `SELECT … FROM tasks`, as the viewer; the rows hold the selected columns by name.
|
||||
* @example
|
||||
* clickup.tasks.query(`SELECT status, COUNT(*) AS n FROM tasks WHERE list = '${LIST_ID}' GROUP BY status`)
|
||||
*/
|
||||
query(sql: string): Promise<Array<Record<string, unknown>>>;
|
||||
/** The workspace's task types. */
|
||||
types(): Promise<TaskType[]>;
|
||||
/** The effective statuses of a list, folder or space. */
|
||||
statuses(selector: StatusSelector): Promise<Status[]>;
|
||||
}
|
||||
export declare function useTasks(selector: TaskSelector | Array<Ref<"task"> | string>, options?: UseQueryOptions<Task[]>): UseQueryResult<Task[]>;
|
||||
/** The rows of `clickup.tasks.query(sql)`, refetched after every task write. */
|
||||
export declare function useTaskQuery(sql: string, options?: UseQueryOptions<Array<Record<string, unknown>>>): UseQueryResult<Array<Record<string, unknown>>>;
|
||||
export declare function useTaskTypes(options?: UseQueryOptions<TaskType[]>): UseQueryResult<TaskType[]>;
|
||||
export declare function useStatuses(selector: StatusSelector, options?: UseQueryOptions<Status[]>): UseQueryResult<Status[]>;
|
||||
Reference in New Issue
Block a user