feat(agents): Add identity and soul template fields to board creation

This commit is contained in:
Abhimanyu Saharan
2026-02-04 20:21:33 +05:30
parent 1c972edb46
commit c3357f92d9
117 changed files with 7899 additions and 1339 deletions

View File

@@ -4,8 +4,17 @@ const nextConfig: NextConfig = {
allowedDevOrigins: [
"http://localhost:3000",
"http://127.0.0.1:3000",
"http://192.168.1.101",
"http://192.168.1.101:3000",
],
images: {
remotePatterns: [
{
protocol: "https",
hostname: "img.clerk.com",
},
],
},
};
export default nextConfig;

View File

@@ -10,11 +10,13 @@
"dependencies": {
"@clerk/nextjs": "^6.37.1",
"@radix-ui/react-dialog": "^1.1.2",
"@radix-ui/react-popover": "^1.1.15",
"@radix-ui/react-select": "^2.2.6",
"@radix-ui/react-tabs": "^1.1.1",
"@radix-ui/react-tooltip": "^1.1.2",
"@tanstack/react-query": "^5.90.20",
"@tanstack/react-table": "^8.21.3",
"cmdk": "^1.1.1",
"next": "16.1.6",
"react": "19.2.3",
"react-dom": "19.2.3"
@@ -2221,6 +2223,43 @@
}
}
},
"node_modules/@radix-ui/react-popover": {
"version": "1.1.15",
"resolved": "https://registry.npmjs.org/@radix-ui/react-popover/-/react-popover-1.1.15.tgz",
"integrity": "sha512-kr0X2+6Yy/vJzLYJUPCZEc8SfQcf+1COFoAqauJm74umQhta9M7lNJHP7QQS3vkvcGLQUbWpMzwrXYwrYztHKA==",
"license": "MIT",
"dependencies": {
"@radix-ui/primitive": "1.1.3",
"@radix-ui/react-compose-refs": "1.1.2",
"@radix-ui/react-context": "1.1.2",
"@radix-ui/react-dismissable-layer": "1.1.11",
"@radix-ui/react-focus-guards": "1.1.3",
"@radix-ui/react-focus-scope": "1.1.7",
"@radix-ui/react-id": "1.1.1",
"@radix-ui/react-popper": "1.2.8",
"@radix-ui/react-portal": "1.1.9",
"@radix-ui/react-presence": "1.1.5",
"@radix-ui/react-primitive": "2.1.3",
"@radix-ui/react-slot": "1.2.3",
"@radix-ui/react-use-controllable-state": "1.2.2",
"aria-hidden": "^1.2.4",
"react-remove-scroll": "^2.6.3"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-popper": {
"version": "1.2.8",
"resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.8.tgz",
@@ -4232,6 +4271,22 @@
"node": ">=6"
}
},
"node_modules/cmdk": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/cmdk/-/cmdk-1.1.1.tgz",
"integrity": "sha512-Vsv7kFaXm+ptHDMZ7izaRsP70GgrW9NBNGswt9OZaVBLlE0SNpDq8eu/VGXyF9r7M0azK3Wy7OlYXsuyYLFzHg==",
"license": "MIT",
"dependencies": {
"@radix-ui/react-compose-refs": "^1.1.1",
"@radix-ui/react-dialog": "^1.1.6",
"@radix-ui/react-id": "^1.1.0",
"@radix-ui/react-primitive": "^2.0.2"
},
"peerDependencies": {
"react": "^18 || ^19 || ^19.0.0-rc",
"react-dom": "^18 || ^19 || ^19.0.0-rc"
}
},
"node_modules/color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",

View File

@@ -12,12 +12,14 @@
},
"dependencies": {
"@clerk/nextjs": "^6.37.1",
"@radix-ui/react-popover": "^1.1.15",
"@radix-ui/react-dialog": "^1.1.2",
"@radix-ui/react-select": "^2.2.6",
"@radix-ui/react-tabs": "^1.1.1",
"@radix-ui/react-tooltip": "^1.1.2",
"@tanstack/react-query": "^5.90.20",
"@tanstack/react-table": "^8.21.3",
"cmdk": "^1.1.1",
"next": "16.1.6",
"react": "19.2.3",
"react-dom": "19.2.3"

View File

@@ -0,0 +1,238 @@
/**
* Generated by orval v8.2.0 🍺
* Do not edit manually.
* Mission Control API
* OpenAPI spec version: 0.1.0
*/
import { useQuery } from "@tanstack/react-query";
import type {
DataTag,
DefinedInitialDataOptions,
DefinedUseQueryResult,
QueryClient,
QueryFunction,
QueryKey,
UndefinedInitialDataOptions,
UseQueryOptions,
UseQueryResult,
} from "@tanstack/react-query";
import type {
ActivityEventRead,
HTTPValidationError,
ListActivityApiV1ActivityGetParams,
} from ".././model";
import { customFetch } from "../../mutator";
type SecondParameter<T extends (...args: never) => unknown> = Parameters<T>[1];
/**
* @summary List Activity
*/
export type listActivityApiV1ActivityGetResponse200 = {
data: ActivityEventRead[];
status: 200;
};
export type listActivityApiV1ActivityGetResponse422 = {
data: HTTPValidationError;
status: 422;
};
export type listActivityApiV1ActivityGetResponseSuccess =
listActivityApiV1ActivityGetResponse200 & {
headers: Headers;
};
export type listActivityApiV1ActivityGetResponseError =
listActivityApiV1ActivityGetResponse422 & {
headers: Headers;
};
export type listActivityApiV1ActivityGetResponse =
| listActivityApiV1ActivityGetResponseSuccess
| listActivityApiV1ActivityGetResponseError;
export const getListActivityApiV1ActivityGetUrl = (
params?: ListActivityApiV1ActivityGetParams,
) => {
const normalizedParams = new URLSearchParams();
Object.entries(params || {}).forEach(([key, value]) => {
if (value !== undefined) {
normalizedParams.append(key, value === null ? "null" : value.toString());
}
});
const stringifiedParams = normalizedParams.toString();
return stringifiedParams.length > 0
? `/api/v1/activity?${stringifiedParams}`
: `/api/v1/activity`;
};
export const listActivityApiV1ActivityGet = async (
params?: ListActivityApiV1ActivityGetParams,
options?: RequestInit,
): Promise<listActivityApiV1ActivityGetResponse> => {
return customFetch<listActivityApiV1ActivityGetResponse>(
getListActivityApiV1ActivityGetUrl(params),
{
...options,
method: "GET",
},
);
};
export const getListActivityApiV1ActivityGetQueryKey = (
params?: ListActivityApiV1ActivityGetParams,
) => {
return [`/api/v1/activity`, ...(params ? [params] : [])] as const;
};
export const getListActivityApiV1ActivityGetQueryOptions = <
TData = Awaited<ReturnType<typeof listActivityApiV1ActivityGet>>,
TError = HTTPValidationError,
>(
params?: ListActivityApiV1ActivityGetParams,
options?: {
query?: Partial<
UseQueryOptions<
Awaited<ReturnType<typeof listActivityApiV1ActivityGet>>,
TError,
TData
>
>;
request?: SecondParameter<typeof customFetch>;
},
) => {
const { query: queryOptions, request: requestOptions } = options ?? {};
const queryKey =
queryOptions?.queryKey ?? getListActivityApiV1ActivityGetQueryKey(params);
const queryFn: QueryFunction<
Awaited<ReturnType<typeof listActivityApiV1ActivityGet>>
> = ({ signal }) =>
listActivityApiV1ActivityGet(params, { signal, ...requestOptions });
return { queryKey, queryFn, ...queryOptions } as UseQueryOptions<
Awaited<ReturnType<typeof listActivityApiV1ActivityGet>>,
TError,
TData
> & { queryKey: DataTag<QueryKey, TData, TError> };
};
export type ListActivityApiV1ActivityGetQueryResult = NonNullable<
Awaited<ReturnType<typeof listActivityApiV1ActivityGet>>
>;
export type ListActivityApiV1ActivityGetQueryError = HTTPValidationError;
export function useListActivityApiV1ActivityGet<
TData = Awaited<ReturnType<typeof listActivityApiV1ActivityGet>>,
TError = HTTPValidationError,
>(
params: undefined | ListActivityApiV1ActivityGetParams,
options: {
query: Partial<
UseQueryOptions<
Awaited<ReturnType<typeof listActivityApiV1ActivityGet>>,
TError,
TData
>
> &
Pick<
DefinedInitialDataOptions<
Awaited<ReturnType<typeof listActivityApiV1ActivityGet>>,
TError,
Awaited<ReturnType<typeof listActivityApiV1ActivityGet>>
>,
"initialData"
>;
request?: SecondParameter<typeof customFetch>;
},
queryClient?: QueryClient,
): DefinedUseQueryResult<TData, TError> & {
queryKey: DataTag<QueryKey, TData, TError>;
};
export function useListActivityApiV1ActivityGet<
TData = Awaited<ReturnType<typeof listActivityApiV1ActivityGet>>,
TError = HTTPValidationError,
>(
params?: ListActivityApiV1ActivityGetParams,
options?: {
query?: Partial<
UseQueryOptions<
Awaited<ReturnType<typeof listActivityApiV1ActivityGet>>,
TError,
TData
>
> &
Pick<
UndefinedInitialDataOptions<
Awaited<ReturnType<typeof listActivityApiV1ActivityGet>>,
TError,
Awaited<ReturnType<typeof listActivityApiV1ActivityGet>>
>,
"initialData"
>;
request?: SecondParameter<typeof customFetch>;
},
queryClient?: QueryClient,
): UseQueryResult<TData, TError> & {
queryKey: DataTag<QueryKey, TData, TError>;
};
export function useListActivityApiV1ActivityGet<
TData = Awaited<ReturnType<typeof listActivityApiV1ActivityGet>>,
TError = HTTPValidationError,
>(
params?: ListActivityApiV1ActivityGetParams,
options?: {
query?: Partial<
UseQueryOptions<
Awaited<ReturnType<typeof listActivityApiV1ActivityGet>>,
TError,
TData
>
>;
request?: SecondParameter<typeof customFetch>;
},
queryClient?: QueryClient,
): UseQueryResult<TData, TError> & {
queryKey: DataTag<QueryKey, TData, TError>;
};
/**
* @summary List Activity
*/
export function useListActivityApiV1ActivityGet<
TData = Awaited<ReturnType<typeof listActivityApiV1ActivityGet>>,
TError = HTTPValidationError,
>(
params?: ListActivityApiV1ActivityGetParams,
options?: {
query?: Partial<
UseQueryOptions<
Awaited<ReturnType<typeof listActivityApiV1ActivityGet>>,
TError,
TData
>
>;
request?: SecondParameter<typeof customFetch>;
},
queryClient?: QueryClient,
): UseQueryResult<TData, TError> & {
queryKey: DataTag<QueryKey, TData, TError>;
} {
const queryOptions = getListActivityApiV1ActivityGetQueryOptions(
params,
options,
);
const query = useQuery(queryOptions, queryClient) as UseQueryResult<
TData,
TError
> & { queryKey: DataTag<QueryKey, TData, TError> };
return { ...query, queryKey: queryOptions.queryKey };
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,121 @@
/**
* Generated by orval v8.2.0 🍺
* Do not edit manually.
* Mission Control API
* OpenAPI spec version: 0.1.0
*/
import { useMutation } from "@tanstack/react-query";
import type {
MutationFunction,
QueryClient,
UseMutationOptions,
UseMutationResult,
} from "@tanstack/react-query";
import type { UserRead } from ".././model";
import { customFetch } from "../../mutator";
type SecondParameter<T extends (...args: never) => unknown> = Parameters<T>[1];
/**
* @summary Bootstrap User
*/
export type bootstrapUserApiV1AuthBootstrapPostResponse200 = {
data: UserRead;
status: 200;
};
export type bootstrapUserApiV1AuthBootstrapPostResponseSuccess =
bootstrapUserApiV1AuthBootstrapPostResponse200 & {
headers: Headers;
};
export type bootstrapUserApiV1AuthBootstrapPostResponse =
bootstrapUserApiV1AuthBootstrapPostResponseSuccess;
export const getBootstrapUserApiV1AuthBootstrapPostUrl = () => {
return `/api/v1/auth/bootstrap`;
};
export const bootstrapUserApiV1AuthBootstrapPost = async (
options?: RequestInit,
): Promise<bootstrapUserApiV1AuthBootstrapPostResponse> => {
return customFetch<bootstrapUserApiV1AuthBootstrapPostResponse>(
getBootstrapUserApiV1AuthBootstrapPostUrl(),
{
...options,
method: "POST",
},
);
};
export const getBootstrapUserApiV1AuthBootstrapPostMutationOptions = <
TError = unknown,
TContext = unknown,
>(options?: {
mutation?: UseMutationOptions<
Awaited<ReturnType<typeof bootstrapUserApiV1AuthBootstrapPost>>,
TError,
void,
TContext
>;
request?: SecondParameter<typeof customFetch>;
}): UseMutationOptions<
Awaited<ReturnType<typeof bootstrapUserApiV1AuthBootstrapPost>>,
TError,
void,
TContext
> => {
const mutationKey = ["bootstrapUserApiV1AuthBootstrapPost"];
const { mutation: mutationOptions, request: requestOptions } = options
? options.mutation &&
"mutationKey" in options.mutation &&
options.mutation.mutationKey
? options
: { ...options, mutation: { ...options.mutation, mutationKey } }
: { mutation: { mutationKey }, request: undefined };
const mutationFn: MutationFunction<
Awaited<ReturnType<typeof bootstrapUserApiV1AuthBootstrapPost>>,
void
> = () => {
return bootstrapUserApiV1AuthBootstrapPost(requestOptions);
};
return { mutationFn, ...mutationOptions };
};
export type BootstrapUserApiV1AuthBootstrapPostMutationResult = NonNullable<
Awaited<ReturnType<typeof bootstrapUserApiV1AuthBootstrapPost>>
>;
export type BootstrapUserApiV1AuthBootstrapPostMutationError = unknown;
/**
* @summary Bootstrap User
*/
export const useBootstrapUserApiV1AuthBootstrapPost = <
TError = unknown,
TContext = unknown,
>(
options?: {
mutation?: UseMutationOptions<
Awaited<ReturnType<typeof bootstrapUserApiV1AuthBootstrapPost>>,
TError,
void,
TContext
>;
request?: SecondParameter<typeof customFetch>;
},
queryClient?: QueryClient,
): UseMutationResult<
Awaited<ReturnType<typeof bootstrapUserApiV1AuthBootstrapPost>>,
TError,
void,
TContext
> => {
return useMutation(
getBootstrapUserApiV1AuthBootstrapPostMutationOptions(options),
queryClient,
);
};

View File

@@ -0,0 +1,767 @@
/**
* Generated by orval v8.2.0 🍺
* Do not edit manually.
* Mission Control API
* OpenAPI spec version: 0.1.0
*/
import { useMutation, useQuery } from "@tanstack/react-query";
import type {
DataTag,
DefinedInitialDataOptions,
DefinedUseQueryResult,
MutationFunction,
QueryClient,
QueryFunction,
QueryKey,
UndefinedInitialDataOptions,
UseMutationOptions,
UseMutationResult,
UseQueryOptions,
UseQueryResult,
} from "@tanstack/react-query";
import type {
BoardCreate,
BoardRead,
BoardUpdate,
DeleteBoardApiV1BoardsBoardIdDelete200,
HTTPValidationError,
} from ".././model";
import { customFetch } from "../../mutator";
type SecondParameter<T extends (...args: never) => unknown> = Parameters<T>[1];
/**
* @summary List Boards
*/
export type listBoardsApiV1BoardsGetResponse200 = {
data: BoardRead[];
status: 200;
};
export type listBoardsApiV1BoardsGetResponse422 = {
data: HTTPValidationError;
status: 422;
};
export type listBoardsApiV1BoardsGetResponseSuccess =
listBoardsApiV1BoardsGetResponse200 & {
headers: Headers;
};
export type listBoardsApiV1BoardsGetResponseError =
listBoardsApiV1BoardsGetResponse422 & {
headers: Headers;
};
export type listBoardsApiV1BoardsGetResponse =
| listBoardsApiV1BoardsGetResponseSuccess
| listBoardsApiV1BoardsGetResponseError;
export const getListBoardsApiV1BoardsGetUrl = () => {
return `/api/v1/boards`;
};
export const listBoardsApiV1BoardsGet = async (
options?: RequestInit,
): Promise<listBoardsApiV1BoardsGetResponse> => {
return customFetch<listBoardsApiV1BoardsGetResponse>(
getListBoardsApiV1BoardsGetUrl(),
{
...options,
method: "GET",
},
);
};
export const getListBoardsApiV1BoardsGetQueryKey = () => {
return [`/api/v1/boards`] as const;
};
export const getListBoardsApiV1BoardsGetQueryOptions = <
TData = Awaited<ReturnType<typeof listBoardsApiV1BoardsGet>>,
TError = HTTPValidationError,
>(options?: {
query?: Partial<
UseQueryOptions<
Awaited<ReturnType<typeof listBoardsApiV1BoardsGet>>,
TError,
TData
>
>;
request?: SecondParameter<typeof customFetch>;
}) => {
const { query: queryOptions, request: requestOptions } = options ?? {};
const queryKey =
queryOptions?.queryKey ?? getListBoardsApiV1BoardsGetQueryKey();
const queryFn: QueryFunction<
Awaited<ReturnType<typeof listBoardsApiV1BoardsGet>>
> = ({ signal }) => listBoardsApiV1BoardsGet({ signal, ...requestOptions });
return { queryKey, queryFn, ...queryOptions } as UseQueryOptions<
Awaited<ReturnType<typeof listBoardsApiV1BoardsGet>>,
TError,
TData
> & { queryKey: DataTag<QueryKey, TData, TError> };
};
export type ListBoardsApiV1BoardsGetQueryResult = NonNullable<
Awaited<ReturnType<typeof listBoardsApiV1BoardsGet>>
>;
export type ListBoardsApiV1BoardsGetQueryError = HTTPValidationError;
export function useListBoardsApiV1BoardsGet<
TData = Awaited<ReturnType<typeof listBoardsApiV1BoardsGet>>,
TError = HTTPValidationError,
>(
options: {
query: Partial<
UseQueryOptions<
Awaited<ReturnType<typeof listBoardsApiV1BoardsGet>>,
TError,
TData
>
> &
Pick<
DefinedInitialDataOptions<
Awaited<ReturnType<typeof listBoardsApiV1BoardsGet>>,
TError,
Awaited<ReturnType<typeof listBoardsApiV1BoardsGet>>
>,
"initialData"
>;
request?: SecondParameter<typeof customFetch>;
},
queryClient?: QueryClient,
): DefinedUseQueryResult<TData, TError> & {
queryKey: DataTag<QueryKey, TData, TError>;
};
export function useListBoardsApiV1BoardsGet<
TData = Awaited<ReturnType<typeof listBoardsApiV1BoardsGet>>,
TError = HTTPValidationError,
>(
options?: {
query?: Partial<
UseQueryOptions<
Awaited<ReturnType<typeof listBoardsApiV1BoardsGet>>,
TError,
TData
>
> &
Pick<
UndefinedInitialDataOptions<
Awaited<ReturnType<typeof listBoardsApiV1BoardsGet>>,
TError,
Awaited<ReturnType<typeof listBoardsApiV1BoardsGet>>
>,
"initialData"
>;
request?: SecondParameter<typeof customFetch>;
},
queryClient?: QueryClient,
): UseQueryResult<TData, TError> & {
queryKey: DataTag<QueryKey, TData, TError>;
};
export function useListBoardsApiV1BoardsGet<
TData = Awaited<ReturnType<typeof listBoardsApiV1BoardsGet>>,
TError = HTTPValidationError,
>(
options?: {
query?: Partial<
UseQueryOptions<
Awaited<ReturnType<typeof listBoardsApiV1BoardsGet>>,
TError,
TData
>
>;
request?: SecondParameter<typeof customFetch>;
},
queryClient?: QueryClient,
): UseQueryResult<TData, TError> & {
queryKey: DataTag<QueryKey, TData, TError>;
};
/**
* @summary List Boards
*/
export function useListBoardsApiV1BoardsGet<
TData = Awaited<ReturnType<typeof listBoardsApiV1BoardsGet>>,
TError = HTTPValidationError,
>(
options?: {
query?: Partial<
UseQueryOptions<
Awaited<ReturnType<typeof listBoardsApiV1BoardsGet>>,
TError,
TData
>
>;
request?: SecondParameter<typeof customFetch>;
},
queryClient?: QueryClient,
): UseQueryResult<TData, TError> & {
queryKey: DataTag<QueryKey, TData, TError>;
} {
const queryOptions = getListBoardsApiV1BoardsGetQueryOptions(options);
const query = useQuery(queryOptions, queryClient) as UseQueryResult<
TData,
TError
> & { queryKey: DataTag<QueryKey, TData, TError> };
return { ...query, queryKey: queryOptions.queryKey };
}
/**
* @summary Create Board
*/
export type createBoardApiV1BoardsPostResponse200 = {
data: BoardRead;
status: 200;
};
export type createBoardApiV1BoardsPostResponse422 = {
data: HTTPValidationError;
status: 422;
};
export type createBoardApiV1BoardsPostResponseSuccess =
createBoardApiV1BoardsPostResponse200 & {
headers: Headers;
};
export type createBoardApiV1BoardsPostResponseError =
createBoardApiV1BoardsPostResponse422 & {
headers: Headers;
};
export type createBoardApiV1BoardsPostResponse =
| createBoardApiV1BoardsPostResponseSuccess
| createBoardApiV1BoardsPostResponseError;
export const getCreateBoardApiV1BoardsPostUrl = () => {
return `/api/v1/boards`;
};
export const createBoardApiV1BoardsPost = async (
boardCreate: BoardCreate,
options?: RequestInit,
): Promise<createBoardApiV1BoardsPostResponse> => {
return customFetch<createBoardApiV1BoardsPostResponse>(
getCreateBoardApiV1BoardsPostUrl(),
{
...options,
method: "POST",
headers: { "Content-Type": "application/json", ...options?.headers },
body: JSON.stringify(boardCreate),
},
);
};
export const getCreateBoardApiV1BoardsPostMutationOptions = <
TError = HTTPValidationError,
TContext = unknown,
>(options?: {
mutation?: UseMutationOptions<
Awaited<ReturnType<typeof createBoardApiV1BoardsPost>>,
TError,
{ data: BoardCreate },
TContext
>;
request?: SecondParameter<typeof customFetch>;
}): UseMutationOptions<
Awaited<ReturnType<typeof createBoardApiV1BoardsPost>>,
TError,
{ data: BoardCreate },
TContext
> => {
const mutationKey = ["createBoardApiV1BoardsPost"];
const { mutation: mutationOptions, request: requestOptions } = options
? options.mutation &&
"mutationKey" in options.mutation &&
options.mutation.mutationKey
? options
: { ...options, mutation: { ...options.mutation, mutationKey } }
: { mutation: { mutationKey }, request: undefined };
const mutationFn: MutationFunction<
Awaited<ReturnType<typeof createBoardApiV1BoardsPost>>,
{ data: BoardCreate }
> = (props) => {
const { data } = props ?? {};
return createBoardApiV1BoardsPost(data, requestOptions);
};
return { mutationFn, ...mutationOptions };
};
export type CreateBoardApiV1BoardsPostMutationResult = NonNullable<
Awaited<ReturnType<typeof createBoardApiV1BoardsPost>>
>;
export type CreateBoardApiV1BoardsPostMutationBody = BoardCreate;
export type CreateBoardApiV1BoardsPostMutationError = HTTPValidationError;
/**
* @summary Create Board
*/
export const useCreateBoardApiV1BoardsPost = <
TError = HTTPValidationError,
TContext = unknown,
>(
options?: {
mutation?: UseMutationOptions<
Awaited<ReturnType<typeof createBoardApiV1BoardsPost>>,
TError,
{ data: BoardCreate },
TContext
>;
request?: SecondParameter<typeof customFetch>;
},
queryClient?: QueryClient,
): UseMutationResult<
Awaited<ReturnType<typeof createBoardApiV1BoardsPost>>,
TError,
{ data: BoardCreate },
TContext
> => {
return useMutation(
getCreateBoardApiV1BoardsPostMutationOptions(options),
queryClient,
);
};
/**
* @summary Get Board
*/
export type getBoardApiV1BoardsBoardIdGetResponse200 = {
data: BoardRead;
status: 200;
};
export type getBoardApiV1BoardsBoardIdGetResponse422 = {
data: HTTPValidationError;
status: 422;
};
export type getBoardApiV1BoardsBoardIdGetResponseSuccess =
getBoardApiV1BoardsBoardIdGetResponse200 & {
headers: Headers;
};
export type getBoardApiV1BoardsBoardIdGetResponseError =
getBoardApiV1BoardsBoardIdGetResponse422 & {
headers: Headers;
};
export type getBoardApiV1BoardsBoardIdGetResponse =
| getBoardApiV1BoardsBoardIdGetResponseSuccess
| getBoardApiV1BoardsBoardIdGetResponseError;
export const getGetBoardApiV1BoardsBoardIdGetUrl = (boardId: string) => {
return `/api/v1/boards/${boardId}`;
};
export const getBoardApiV1BoardsBoardIdGet = async (
boardId: string,
options?: RequestInit,
): Promise<getBoardApiV1BoardsBoardIdGetResponse> => {
return customFetch<getBoardApiV1BoardsBoardIdGetResponse>(
getGetBoardApiV1BoardsBoardIdGetUrl(boardId),
{
...options,
method: "GET",
},
);
};
export const getGetBoardApiV1BoardsBoardIdGetQueryKey = (boardId: string) => {
return [`/api/v1/boards/${boardId}`] as const;
};
export const getGetBoardApiV1BoardsBoardIdGetQueryOptions = <
TData = Awaited<ReturnType<typeof getBoardApiV1BoardsBoardIdGet>>,
TError = HTTPValidationError,
>(
boardId: string,
options?: {
query?: Partial<
UseQueryOptions<
Awaited<ReturnType<typeof getBoardApiV1BoardsBoardIdGet>>,
TError,
TData
>
>;
request?: SecondParameter<typeof customFetch>;
},
) => {
const { query: queryOptions, request: requestOptions } = options ?? {};
const queryKey =
queryOptions?.queryKey ?? getGetBoardApiV1BoardsBoardIdGetQueryKey(boardId);
const queryFn: QueryFunction<
Awaited<ReturnType<typeof getBoardApiV1BoardsBoardIdGet>>
> = ({ signal }) =>
getBoardApiV1BoardsBoardIdGet(boardId, { signal, ...requestOptions });
return {
queryKey,
queryFn,
enabled: !!boardId,
...queryOptions,
} as UseQueryOptions<
Awaited<ReturnType<typeof getBoardApiV1BoardsBoardIdGet>>,
TError,
TData
> & { queryKey: DataTag<QueryKey, TData, TError> };
};
export type GetBoardApiV1BoardsBoardIdGetQueryResult = NonNullable<
Awaited<ReturnType<typeof getBoardApiV1BoardsBoardIdGet>>
>;
export type GetBoardApiV1BoardsBoardIdGetQueryError = HTTPValidationError;
export function useGetBoardApiV1BoardsBoardIdGet<
TData = Awaited<ReturnType<typeof getBoardApiV1BoardsBoardIdGet>>,
TError = HTTPValidationError,
>(
boardId: string,
options: {
query: Partial<
UseQueryOptions<
Awaited<ReturnType<typeof getBoardApiV1BoardsBoardIdGet>>,
TError,
TData
>
> &
Pick<
DefinedInitialDataOptions<
Awaited<ReturnType<typeof getBoardApiV1BoardsBoardIdGet>>,
TError,
Awaited<ReturnType<typeof getBoardApiV1BoardsBoardIdGet>>
>,
"initialData"
>;
request?: SecondParameter<typeof customFetch>;
},
queryClient?: QueryClient,
): DefinedUseQueryResult<TData, TError> & {
queryKey: DataTag<QueryKey, TData, TError>;
};
export function useGetBoardApiV1BoardsBoardIdGet<
TData = Awaited<ReturnType<typeof getBoardApiV1BoardsBoardIdGet>>,
TError = HTTPValidationError,
>(
boardId: string,
options?: {
query?: Partial<
UseQueryOptions<
Awaited<ReturnType<typeof getBoardApiV1BoardsBoardIdGet>>,
TError,
TData
>
> &
Pick<
UndefinedInitialDataOptions<
Awaited<ReturnType<typeof getBoardApiV1BoardsBoardIdGet>>,
TError,
Awaited<ReturnType<typeof getBoardApiV1BoardsBoardIdGet>>
>,
"initialData"
>;
request?: SecondParameter<typeof customFetch>;
},
queryClient?: QueryClient,
): UseQueryResult<TData, TError> & {
queryKey: DataTag<QueryKey, TData, TError>;
};
export function useGetBoardApiV1BoardsBoardIdGet<
TData = Awaited<ReturnType<typeof getBoardApiV1BoardsBoardIdGet>>,
TError = HTTPValidationError,
>(
boardId: string,
options?: {
query?: Partial<
UseQueryOptions<
Awaited<ReturnType<typeof getBoardApiV1BoardsBoardIdGet>>,
TError,
TData
>
>;
request?: SecondParameter<typeof customFetch>;
},
queryClient?: QueryClient,
): UseQueryResult<TData, TError> & {
queryKey: DataTag<QueryKey, TData, TError>;
};
/**
* @summary Get Board
*/
export function useGetBoardApiV1BoardsBoardIdGet<
TData = Awaited<ReturnType<typeof getBoardApiV1BoardsBoardIdGet>>,
TError = HTTPValidationError,
>(
boardId: string,
options?: {
query?: Partial<
UseQueryOptions<
Awaited<ReturnType<typeof getBoardApiV1BoardsBoardIdGet>>,
TError,
TData
>
>;
request?: SecondParameter<typeof customFetch>;
},
queryClient?: QueryClient,
): UseQueryResult<TData, TError> & {
queryKey: DataTag<QueryKey, TData, TError>;
} {
const queryOptions = getGetBoardApiV1BoardsBoardIdGetQueryOptions(
boardId,
options,
);
const query = useQuery(queryOptions, queryClient) as UseQueryResult<
TData,
TError
> & { queryKey: DataTag<QueryKey, TData, TError> };
return { ...query, queryKey: queryOptions.queryKey };
}
/**
* @summary Update Board
*/
export type updateBoardApiV1BoardsBoardIdPatchResponse200 = {
data: BoardRead;
status: 200;
};
export type updateBoardApiV1BoardsBoardIdPatchResponse422 = {
data: HTTPValidationError;
status: 422;
};
export type updateBoardApiV1BoardsBoardIdPatchResponseSuccess =
updateBoardApiV1BoardsBoardIdPatchResponse200 & {
headers: Headers;
};
export type updateBoardApiV1BoardsBoardIdPatchResponseError =
updateBoardApiV1BoardsBoardIdPatchResponse422 & {
headers: Headers;
};
export type updateBoardApiV1BoardsBoardIdPatchResponse =
| updateBoardApiV1BoardsBoardIdPatchResponseSuccess
| updateBoardApiV1BoardsBoardIdPatchResponseError;
export const getUpdateBoardApiV1BoardsBoardIdPatchUrl = (boardId: string) => {
return `/api/v1/boards/${boardId}`;
};
export const updateBoardApiV1BoardsBoardIdPatch = async (
boardId: string,
boardUpdate: BoardUpdate,
options?: RequestInit,
): Promise<updateBoardApiV1BoardsBoardIdPatchResponse> => {
return customFetch<updateBoardApiV1BoardsBoardIdPatchResponse>(
getUpdateBoardApiV1BoardsBoardIdPatchUrl(boardId),
{
...options,
method: "PATCH",
headers: { "Content-Type": "application/json", ...options?.headers },
body: JSON.stringify(boardUpdate),
},
);
};
export const getUpdateBoardApiV1BoardsBoardIdPatchMutationOptions = <
TError = HTTPValidationError,
TContext = unknown,
>(options?: {
mutation?: UseMutationOptions<
Awaited<ReturnType<typeof updateBoardApiV1BoardsBoardIdPatch>>,
TError,
{ boardId: string; data: BoardUpdate },
TContext
>;
request?: SecondParameter<typeof customFetch>;
}): UseMutationOptions<
Awaited<ReturnType<typeof updateBoardApiV1BoardsBoardIdPatch>>,
TError,
{ boardId: string; data: BoardUpdate },
TContext
> => {
const mutationKey = ["updateBoardApiV1BoardsBoardIdPatch"];
const { mutation: mutationOptions, request: requestOptions } = options
? options.mutation &&
"mutationKey" in options.mutation &&
options.mutation.mutationKey
? options
: { ...options, mutation: { ...options.mutation, mutationKey } }
: { mutation: { mutationKey }, request: undefined };
const mutationFn: MutationFunction<
Awaited<ReturnType<typeof updateBoardApiV1BoardsBoardIdPatch>>,
{ boardId: string; data: BoardUpdate }
> = (props) => {
const { boardId, data } = props ?? {};
return updateBoardApiV1BoardsBoardIdPatch(boardId, data, requestOptions);
};
return { mutationFn, ...mutationOptions };
};
export type UpdateBoardApiV1BoardsBoardIdPatchMutationResult = NonNullable<
Awaited<ReturnType<typeof updateBoardApiV1BoardsBoardIdPatch>>
>;
export type UpdateBoardApiV1BoardsBoardIdPatchMutationBody = BoardUpdate;
export type UpdateBoardApiV1BoardsBoardIdPatchMutationError =
HTTPValidationError;
/**
* @summary Update Board
*/
export const useUpdateBoardApiV1BoardsBoardIdPatch = <
TError = HTTPValidationError,
TContext = unknown,
>(
options?: {
mutation?: UseMutationOptions<
Awaited<ReturnType<typeof updateBoardApiV1BoardsBoardIdPatch>>,
TError,
{ boardId: string; data: BoardUpdate },
TContext
>;
request?: SecondParameter<typeof customFetch>;
},
queryClient?: QueryClient,
): UseMutationResult<
Awaited<ReturnType<typeof updateBoardApiV1BoardsBoardIdPatch>>,
TError,
{ boardId: string; data: BoardUpdate },
TContext
> => {
return useMutation(
getUpdateBoardApiV1BoardsBoardIdPatchMutationOptions(options),
queryClient,
);
};
/**
* @summary Delete Board
*/
export type deleteBoardApiV1BoardsBoardIdDeleteResponse200 = {
data: DeleteBoardApiV1BoardsBoardIdDelete200;
status: 200;
};
export type deleteBoardApiV1BoardsBoardIdDeleteResponse422 = {
data: HTTPValidationError;
status: 422;
};
export type deleteBoardApiV1BoardsBoardIdDeleteResponseSuccess =
deleteBoardApiV1BoardsBoardIdDeleteResponse200 & {
headers: Headers;
};
export type deleteBoardApiV1BoardsBoardIdDeleteResponseError =
deleteBoardApiV1BoardsBoardIdDeleteResponse422 & {
headers: Headers;
};
export type deleteBoardApiV1BoardsBoardIdDeleteResponse =
| deleteBoardApiV1BoardsBoardIdDeleteResponseSuccess
| deleteBoardApiV1BoardsBoardIdDeleteResponseError;
export const getDeleteBoardApiV1BoardsBoardIdDeleteUrl = (boardId: string) => {
return `/api/v1/boards/${boardId}`;
};
export const deleteBoardApiV1BoardsBoardIdDelete = async (
boardId: string,
options?: RequestInit,
): Promise<deleteBoardApiV1BoardsBoardIdDeleteResponse> => {
return customFetch<deleteBoardApiV1BoardsBoardIdDeleteResponse>(
getDeleteBoardApiV1BoardsBoardIdDeleteUrl(boardId),
{
...options,
method: "DELETE",
},
);
};
export const getDeleteBoardApiV1BoardsBoardIdDeleteMutationOptions = <
TError = HTTPValidationError,
TContext = unknown,
>(options?: {
mutation?: UseMutationOptions<
Awaited<ReturnType<typeof deleteBoardApiV1BoardsBoardIdDelete>>,
TError,
{ boardId: string },
TContext
>;
request?: SecondParameter<typeof customFetch>;
}): UseMutationOptions<
Awaited<ReturnType<typeof deleteBoardApiV1BoardsBoardIdDelete>>,
TError,
{ boardId: string },
TContext
> => {
const mutationKey = ["deleteBoardApiV1BoardsBoardIdDelete"];
const { mutation: mutationOptions, request: requestOptions } = options
? options.mutation &&
"mutationKey" in options.mutation &&
options.mutation.mutationKey
? options
: { ...options, mutation: { ...options.mutation, mutationKey } }
: { mutation: { mutationKey }, request: undefined };
const mutationFn: MutationFunction<
Awaited<ReturnType<typeof deleteBoardApiV1BoardsBoardIdDelete>>,
{ boardId: string }
> = (props) => {
const { boardId } = props ?? {};
return deleteBoardApiV1BoardsBoardIdDelete(boardId, requestOptions);
};
return { mutationFn, ...mutationOptions };
};
export type DeleteBoardApiV1BoardsBoardIdDeleteMutationResult = NonNullable<
Awaited<ReturnType<typeof deleteBoardApiV1BoardsBoardIdDelete>>
>;
export type DeleteBoardApiV1BoardsBoardIdDeleteMutationError =
HTTPValidationError;
/**
* @summary Delete Board
*/
export const useDeleteBoardApiV1BoardsBoardIdDelete = <
TError = HTTPValidationError,
TContext = unknown,
>(
options?: {
mutation?: UseMutationOptions<
Awaited<ReturnType<typeof deleteBoardApiV1BoardsBoardIdDelete>>,
TError,
{ boardId: string },
TContext
>;
request?: SecondParameter<typeof customFetch>;
},
queryClient?: QueryClient,
): UseMutationResult<
Awaited<ReturnType<typeof deleteBoardApiV1BoardsBoardIdDelete>>,
TError,
{ boardId: string },
TContext
> => {
return useMutation(
getDeleteBoardApiV1BoardsBoardIdDeleteMutationOptions(options),
queryClient,
);
};

View File

@@ -0,0 +1,515 @@
/**
* Generated by orval v8.2.0 🍺
* Do not edit manually.
* Mission Control API
* OpenAPI spec version: 0.1.0
*/
import { useQuery } from "@tanstack/react-query";
import type {
DataTag,
DefinedInitialDataOptions,
DefinedUseQueryResult,
QueryClient,
QueryFunction,
QueryKey,
UndefinedInitialDataOptions,
UseQueryOptions,
UseQueryResult,
} from "@tanstack/react-query";
import type {
HealthHealthGet200,
HealthzHealthzGet200,
ReadyzReadyzGet200,
} from ".././model";
import { customFetch } from "../../mutator";
type SecondParameter<T extends (...args: never) => unknown> = Parameters<T>[1];
/**
* @summary Health
*/
export type healthHealthGetResponse200 = {
data: HealthHealthGet200;
status: 200;
};
export type healthHealthGetResponseSuccess = healthHealthGetResponse200 & {
headers: Headers;
};
export type healthHealthGetResponse = healthHealthGetResponseSuccess;
export const getHealthHealthGetUrl = () => {
return `/health`;
};
export const healthHealthGet = async (
options?: RequestInit,
): Promise<healthHealthGetResponse> => {
return customFetch<healthHealthGetResponse>(getHealthHealthGetUrl(), {
...options,
method: "GET",
});
};
export const getHealthHealthGetQueryKey = () => {
return [`/health`] as const;
};
export const getHealthHealthGetQueryOptions = <
TData = Awaited<ReturnType<typeof healthHealthGet>>,
TError = unknown,
>(options?: {
query?: Partial<
UseQueryOptions<Awaited<ReturnType<typeof healthHealthGet>>, TError, TData>
>;
request?: SecondParameter<typeof customFetch>;
}) => {
const { query: queryOptions, request: requestOptions } = options ?? {};
const queryKey = queryOptions?.queryKey ?? getHealthHealthGetQueryKey();
const queryFn: QueryFunction<Awaited<ReturnType<typeof healthHealthGet>>> = ({
signal,
}) => healthHealthGet({ signal, ...requestOptions });
return { queryKey, queryFn, ...queryOptions } as UseQueryOptions<
Awaited<ReturnType<typeof healthHealthGet>>,
TError,
TData
> & { queryKey: DataTag<QueryKey, TData, TError> };
};
export type HealthHealthGetQueryResult = NonNullable<
Awaited<ReturnType<typeof healthHealthGet>>
>;
export type HealthHealthGetQueryError = unknown;
export function useHealthHealthGet<
TData = Awaited<ReturnType<typeof healthHealthGet>>,
TError = unknown,
>(
options: {
query: Partial<
UseQueryOptions<
Awaited<ReturnType<typeof healthHealthGet>>,
TError,
TData
>
> &
Pick<
DefinedInitialDataOptions<
Awaited<ReturnType<typeof healthHealthGet>>,
TError,
Awaited<ReturnType<typeof healthHealthGet>>
>,
"initialData"
>;
request?: SecondParameter<typeof customFetch>;
},
queryClient?: QueryClient,
): DefinedUseQueryResult<TData, TError> & {
queryKey: DataTag<QueryKey, TData, TError>;
};
export function useHealthHealthGet<
TData = Awaited<ReturnType<typeof healthHealthGet>>,
TError = unknown,
>(
options?: {
query?: Partial<
UseQueryOptions<
Awaited<ReturnType<typeof healthHealthGet>>,
TError,
TData
>
> &
Pick<
UndefinedInitialDataOptions<
Awaited<ReturnType<typeof healthHealthGet>>,
TError,
Awaited<ReturnType<typeof healthHealthGet>>
>,
"initialData"
>;
request?: SecondParameter<typeof customFetch>;
},
queryClient?: QueryClient,
): UseQueryResult<TData, TError> & {
queryKey: DataTag<QueryKey, TData, TError>;
};
export function useHealthHealthGet<
TData = Awaited<ReturnType<typeof healthHealthGet>>,
TError = unknown,
>(
options?: {
query?: Partial<
UseQueryOptions<
Awaited<ReturnType<typeof healthHealthGet>>,
TError,
TData
>
>;
request?: SecondParameter<typeof customFetch>;
},
queryClient?: QueryClient,
): UseQueryResult<TData, TError> & {
queryKey: DataTag<QueryKey, TData, TError>;
};
/**
* @summary Health
*/
export function useHealthHealthGet<
TData = Awaited<ReturnType<typeof healthHealthGet>>,
TError = unknown,
>(
options?: {
query?: Partial<
UseQueryOptions<
Awaited<ReturnType<typeof healthHealthGet>>,
TError,
TData
>
>;
request?: SecondParameter<typeof customFetch>;
},
queryClient?: QueryClient,
): UseQueryResult<TData, TError> & {
queryKey: DataTag<QueryKey, TData, TError>;
} {
const queryOptions = getHealthHealthGetQueryOptions(options);
const query = useQuery(queryOptions, queryClient) as UseQueryResult<
TData,
TError
> & { queryKey: DataTag<QueryKey, TData, TError> };
return { ...query, queryKey: queryOptions.queryKey };
}
/**
* @summary Healthz
*/
export type healthzHealthzGetResponse200 = {
data: HealthzHealthzGet200;
status: 200;
};
export type healthzHealthzGetResponseSuccess = healthzHealthzGetResponse200 & {
headers: Headers;
};
export type healthzHealthzGetResponse = healthzHealthzGetResponseSuccess;
export const getHealthzHealthzGetUrl = () => {
return `/healthz`;
};
export const healthzHealthzGet = async (
options?: RequestInit,
): Promise<healthzHealthzGetResponse> => {
return customFetch<healthzHealthzGetResponse>(getHealthzHealthzGetUrl(), {
...options,
method: "GET",
});
};
export const getHealthzHealthzGetQueryKey = () => {
return [`/healthz`] as const;
};
export const getHealthzHealthzGetQueryOptions = <
TData = Awaited<ReturnType<typeof healthzHealthzGet>>,
TError = unknown,
>(options?: {
query?: Partial<
UseQueryOptions<
Awaited<ReturnType<typeof healthzHealthzGet>>,
TError,
TData
>
>;
request?: SecondParameter<typeof customFetch>;
}) => {
const { query: queryOptions, request: requestOptions } = options ?? {};
const queryKey = queryOptions?.queryKey ?? getHealthzHealthzGetQueryKey();
const queryFn: QueryFunction<
Awaited<ReturnType<typeof healthzHealthzGet>>
> = ({ signal }) => healthzHealthzGet({ signal, ...requestOptions });
return { queryKey, queryFn, ...queryOptions } as UseQueryOptions<
Awaited<ReturnType<typeof healthzHealthzGet>>,
TError,
TData
> & { queryKey: DataTag<QueryKey, TData, TError> };
};
export type HealthzHealthzGetQueryResult = NonNullable<
Awaited<ReturnType<typeof healthzHealthzGet>>
>;
export type HealthzHealthzGetQueryError = unknown;
export function useHealthzHealthzGet<
TData = Awaited<ReturnType<typeof healthzHealthzGet>>,
TError = unknown,
>(
options: {
query: Partial<
UseQueryOptions<
Awaited<ReturnType<typeof healthzHealthzGet>>,
TError,
TData
>
> &
Pick<
DefinedInitialDataOptions<
Awaited<ReturnType<typeof healthzHealthzGet>>,
TError,
Awaited<ReturnType<typeof healthzHealthzGet>>
>,
"initialData"
>;
request?: SecondParameter<typeof customFetch>;
},
queryClient?: QueryClient,
): DefinedUseQueryResult<TData, TError> & {
queryKey: DataTag<QueryKey, TData, TError>;
};
export function useHealthzHealthzGet<
TData = Awaited<ReturnType<typeof healthzHealthzGet>>,
TError = unknown,
>(
options?: {
query?: Partial<
UseQueryOptions<
Awaited<ReturnType<typeof healthzHealthzGet>>,
TError,
TData
>
> &
Pick<
UndefinedInitialDataOptions<
Awaited<ReturnType<typeof healthzHealthzGet>>,
TError,
Awaited<ReturnType<typeof healthzHealthzGet>>
>,
"initialData"
>;
request?: SecondParameter<typeof customFetch>;
},
queryClient?: QueryClient,
): UseQueryResult<TData, TError> & {
queryKey: DataTag<QueryKey, TData, TError>;
};
export function useHealthzHealthzGet<
TData = Awaited<ReturnType<typeof healthzHealthzGet>>,
TError = unknown,
>(
options?: {
query?: Partial<
UseQueryOptions<
Awaited<ReturnType<typeof healthzHealthzGet>>,
TError,
TData
>
>;
request?: SecondParameter<typeof customFetch>;
},
queryClient?: QueryClient,
): UseQueryResult<TData, TError> & {
queryKey: DataTag<QueryKey, TData, TError>;
};
/**
* @summary Healthz
*/
export function useHealthzHealthzGet<
TData = Awaited<ReturnType<typeof healthzHealthzGet>>,
TError = unknown,
>(
options?: {
query?: Partial<
UseQueryOptions<
Awaited<ReturnType<typeof healthzHealthzGet>>,
TError,
TData
>
>;
request?: SecondParameter<typeof customFetch>;
},
queryClient?: QueryClient,
): UseQueryResult<TData, TError> & {
queryKey: DataTag<QueryKey, TData, TError>;
} {
const queryOptions = getHealthzHealthzGetQueryOptions(options);
const query = useQuery(queryOptions, queryClient) as UseQueryResult<
TData,
TError
> & { queryKey: DataTag<QueryKey, TData, TError> };
return { ...query, queryKey: queryOptions.queryKey };
}
/**
* @summary Readyz
*/
export type readyzReadyzGetResponse200 = {
data: ReadyzReadyzGet200;
status: 200;
};
export type readyzReadyzGetResponseSuccess = readyzReadyzGetResponse200 & {
headers: Headers;
};
export type readyzReadyzGetResponse = readyzReadyzGetResponseSuccess;
export const getReadyzReadyzGetUrl = () => {
return `/readyz`;
};
export const readyzReadyzGet = async (
options?: RequestInit,
): Promise<readyzReadyzGetResponse> => {
return customFetch<readyzReadyzGetResponse>(getReadyzReadyzGetUrl(), {
...options,
method: "GET",
});
};
export const getReadyzReadyzGetQueryKey = () => {
return [`/readyz`] as const;
};
export const getReadyzReadyzGetQueryOptions = <
TData = Awaited<ReturnType<typeof readyzReadyzGet>>,
TError = unknown,
>(options?: {
query?: Partial<
UseQueryOptions<Awaited<ReturnType<typeof readyzReadyzGet>>, TError, TData>
>;
request?: SecondParameter<typeof customFetch>;
}) => {
const { query: queryOptions, request: requestOptions } = options ?? {};
const queryKey = queryOptions?.queryKey ?? getReadyzReadyzGetQueryKey();
const queryFn: QueryFunction<Awaited<ReturnType<typeof readyzReadyzGet>>> = ({
signal,
}) => readyzReadyzGet({ signal, ...requestOptions });
return { queryKey, queryFn, ...queryOptions } as UseQueryOptions<
Awaited<ReturnType<typeof readyzReadyzGet>>,
TError,
TData
> & { queryKey: DataTag<QueryKey, TData, TError> };
};
export type ReadyzReadyzGetQueryResult = NonNullable<
Awaited<ReturnType<typeof readyzReadyzGet>>
>;
export type ReadyzReadyzGetQueryError = unknown;
export function useReadyzReadyzGet<
TData = Awaited<ReturnType<typeof readyzReadyzGet>>,
TError = unknown,
>(
options: {
query: Partial<
UseQueryOptions<
Awaited<ReturnType<typeof readyzReadyzGet>>,
TError,
TData
>
> &
Pick<
DefinedInitialDataOptions<
Awaited<ReturnType<typeof readyzReadyzGet>>,
TError,
Awaited<ReturnType<typeof readyzReadyzGet>>
>,
"initialData"
>;
request?: SecondParameter<typeof customFetch>;
},
queryClient?: QueryClient,
): DefinedUseQueryResult<TData, TError> & {
queryKey: DataTag<QueryKey, TData, TError>;
};
export function useReadyzReadyzGet<
TData = Awaited<ReturnType<typeof readyzReadyzGet>>,
TError = unknown,
>(
options?: {
query?: Partial<
UseQueryOptions<
Awaited<ReturnType<typeof readyzReadyzGet>>,
TError,
TData
>
> &
Pick<
UndefinedInitialDataOptions<
Awaited<ReturnType<typeof readyzReadyzGet>>,
TError,
Awaited<ReturnType<typeof readyzReadyzGet>>
>,
"initialData"
>;
request?: SecondParameter<typeof customFetch>;
},
queryClient?: QueryClient,
): UseQueryResult<TData, TError> & {
queryKey: DataTag<QueryKey, TData, TError>;
};
export function useReadyzReadyzGet<
TData = Awaited<ReturnType<typeof readyzReadyzGet>>,
TError = unknown,
>(
options?: {
query?: Partial<
UseQueryOptions<
Awaited<ReturnType<typeof readyzReadyzGet>>,
TError,
TData
>
>;
request?: SecondParameter<typeof customFetch>;
},
queryClient?: QueryClient,
): UseQueryResult<TData, TError> & {
queryKey: DataTag<QueryKey, TData, TError>;
};
/**
* @summary Readyz
*/
export function useReadyzReadyzGet<
TData = Awaited<ReturnType<typeof readyzReadyzGet>>,
TError = unknown,
>(
options?: {
query?: Partial<
UseQueryOptions<
Awaited<ReturnType<typeof readyzReadyzGet>>,
TError,
TData
>
>;
request?: SecondParameter<typeof customFetch>;
},
queryClient?: QueryClient,
): UseQueryResult<TData, TError> & {
queryKey: DataTag<QueryKey, TData, TError>;
} {
const queryOptions = getReadyzReadyzGetQueryOptions(options);
const query = useQuery(queryOptions, queryClient) as UseQueryResult<
TData,
TError
> & { queryKey: DataTag<QueryKey, TData, TError> };
return { ...query, queryKey: queryOptions.queryKey };
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,15 @@
/**
* Generated by orval v8.2.0 🍺
* Do not edit manually.
* Mission Control API
* OpenAPI spec version: 0.1.0
*/
export interface ActivityEventRead {
id: string;
event_type: string;
message: string | null;
agent_id: string | null;
task_id: string | null;
created_at: string;
}

View File

@@ -0,0 +1,14 @@
/**
* Generated by orval v8.2.0 🍺
* Do not edit manually.
* Mission Control API
* OpenAPI spec version: 0.1.0
*/
import type { AgentCreateHeartbeatConfig } from "./agentCreateHeartbeatConfig";
export interface AgentCreate {
board_id?: string | null;
name: string;
status?: string;
heartbeat_config?: AgentCreateHeartbeatConfig;
}

View File

@@ -0,0 +1,8 @@
/**
* Generated by orval v8.2.0 🍺
* Do not edit manually.
* Mission Control API
* OpenAPI spec version: 0.1.0
*/
export type AgentCreateHeartbeatConfig = { [key: string]: unknown } | null;

View File

@@ -0,0 +1,10 @@
/**
* Generated by orval v8.2.0 🍺
* Do not edit manually.
* Mission Control API
* OpenAPI spec version: 0.1.0
*/
export interface AgentDeleteConfirm {
token: string;
}

View File

@@ -0,0 +1,10 @@
/**
* Generated by orval v8.2.0 🍺
* Do not edit manually.
* Mission Control API
* OpenAPI spec version: 0.1.0
*/
export interface AgentHeartbeat {
status?: string | null;
}

View File

@@ -0,0 +1,12 @@
/**
* Generated by orval v8.2.0 🍺
* Do not edit manually.
* Mission Control API
* OpenAPI spec version: 0.1.0
*/
export interface AgentHeartbeatCreate {
status?: string | null;
name: string;
board_id?: string | null;
}

View File

@@ -0,0 +1,11 @@
/**
* Generated by orval v8.2.0 🍺
* Do not edit manually.
* Mission Control API
* OpenAPI spec version: 0.1.0
*/
export interface AgentProvisionConfirm {
token: string;
action?: string | null;
}

View File

@@ -0,0 +1,19 @@
/**
* Generated by orval v8.2.0 🍺
* Do not edit manually.
* Mission Control API
* OpenAPI spec version: 0.1.0
*/
import type { AgentReadHeartbeatConfig } from "./agentReadHeartbeatConfig";
export interface AgentRead {
board_id?: string | null;
name: string;
status?: string;
heartbeat_config?: AgentReadHeartbeatConfig;
id: string;
openclaw_session_id?: string | null;
last_seen_at: string | null;
created_at: string;
updated_at: string;
}

View File

@@ -0,0 +1,8 @@
/**
* Generated by orval v8.2.0 🍺
* Do not edit manually.
* Mission Control API
* OpenAPI spec version: 0.1.0
*/
export type AgentReadHeartbeatConfig = { [key: string]: unknown } | null;

View File

@@ -0,0 +1,14 @@
/**
* Generated by orval v8.2.0 🍺
* Do not edit manually.
* Mission Control API
* OpenAPI spec version: 0.1.0
*/
import type { AgentUpdateHeartbeatConfig } from "./agentUpdateHeartbeatConfig";
export interface AgentUpdate {
board_id?: string | null;
name?: string | null;
status?: string | null;
heartbeat_config?: AgentUpdateHeartbeatConfig;
}

View File

@@ -0,0 +1,8 @@
/**
* Generated by orval v8.2.0 🍺
* Do not edit manually.
* Mission Control API
* OpenAPI spec version: 0.1.0
*/
export type AgentUpdateHeartbeatConfig = { [key: string]: unknown } | null;

View File

@@ -0,0 +1,17 @@
/**
* Generated by orval v8.2.0 🍺
* Do not edit manually.
* Mission Control API
* OpenAPI spec version: 0.1.0
*/
export interface BoardCreate {
name: string;
slug: string;
gateway_url?: string | null;
gateway_main_session_key?: string | null;
gateway_workspace_root?: string | null;
identity_template?: string | null;
soul_template?: string | null;
gateway_token?: string | null;
}

View File

@@ -0,0 +1,19 @@
/**
* Generated by orval v8.2.0 🍺
* Do not edit manually.
* Mission Control API
* OpenAPI spec version: 0.1.0
*/
export interface BoardRead {
name: string;
slug: string;
gateway_url?: string | null;
gateway_main_session_key?: string | null;
gateway_workspace_root?: string | null;
identity_template?: string | null;
soul_template?: string | null;
id: string;
created_at: string;
updated_at: string;
}

View File

@@ -0,0 +1,17 @@
/**
* Generated by orval v8.2.0 🍺
* Do not edit manually.
* Mission Control API
* OpenAPI spec version: 0.1.0
*/
export interface BoardUpdate {
name?: string | null;
slug?: string | null;
gateway_url?: string | null;
gateway_token?: string | null;
gateway_main_session_key?: string | null;
gateway_workspace_root?: string | null;
identity_template?: string | null;
soul_template?: string | null;
}

View File

@@ -0,0 +1,10 @@
/**
* Generated by orval v8.2.0 🍺
* Do not edit manually.
* Mission Control API
* OpenAPI spec version: 0.1.0
*/
export type ConfirmDeleteAgentApiV1AgentsAgentIdDeleteConfirmPost200 = {
[key: string]: boolean;
};

View File

@@ -0,0 +1,10 @@
/**
* Generated by orval v8.2.0 🍺
* Do not edit manually.
* Mission Control API
* OpenAPI spec version: 0.1.0
*/
export type ConfirmProvisionAgentApiV1AgentsAgentIdProvisionConfirmPost200 = {
[key: string]: boolean;
};

View File

@@ -0,0 +1,8 @@
/**
* Generated by orval v8.2.0 🍺
* Do not edit manually.
* Mission Control API
* OpenAPI spec version: 0.1.0
*/
export type DeleteAgentApiV1AgentsAgentIdDelete200 = { [key: string]: boolean };

View File

@@ -0,0 +1,8 @@
/**
* Generated by orval v8.2.0 🍺
* Do not edit manually.
* Mission Control API
* OpenAPI spec version: 0.1.0
*/
export type DeleteBoardApiV1BoardsBoardIdDelete200 = { [key: string]: boolean };

View File

@@ -0,0 +1,10 @@
/**
* Generated by orval v8.2.0 🍺
* Do not edit manually.
* Mission Control API
* OpenAPI spec version: 0.1.0
*/
export type DeleteTaskApiV1BoardsBoardIdTasksTaskIdDelete200 = {
[key: string]: boolean;
};

View File

@@ -0,0 +1,10 @@
/**
* Generated by orval v8.2.0 🍺
* Do not edit manually.
* Mission Control API
* OpenAPI spec version: 0.1.0
*/
export type GatewayCommandsApiV1GatewayCommandsGet200 = {
[key: string]: unknown;
};

View File

@@ -0,0 +1,8 @@
/**
* Generated by orval v8.2.0 🍺
* Do not edit manually.
* Mission Control API
* OpenAPI spec version: 0.1.0
*/
export type GatewayStatusApiV1GatewayStatusGet200 = { [key: string]: unknown };

View File

@@ -0,0 +1,10 @@
/**
* Generated by orval v8.2.0 🍺
* Do not edit manually.
* Mission Control API
* OpenAPI spec version: 0.1.0
*/
export type GatewayStatusApiV1GatewayStatusGetParams = {
board_id?: string | null;
};

View File

@@ -0,0 +1,10 @@
/**
* Generated by orval v8.2.0 🍺
* Do not edit manually.
* Mission Control API
* OpenAPI spec version: 0.1.0
*/
export type GetGatewaySessionApiV1GatewaySessionsSessionIdGet200 = {
[key: string]: unknown;
};

View File

@@ -0,0 +1,10 @@
/**
* Generated by orval v8.2.0 🍺
* Do not edit manually.
* Mission Control API
* OpenAPI spec version: 0.1.0
*/
export type GetGatewaySessionApiV1GatewaySessionsSessionIdGetParams = {
board_id?: string | null;
};

View File

@@ -0,0 +1,10 @@
/**
* Generated by orval v8.2.0 🍺
* Do not edit manually.
* Mission Control API
* OpenAPI spec version: 0.1.0
*/
export type GetSessionHistoryApiV1GatewaySessionsSessionIdHistoryGet200 = {
[key: string]: unknown;
};

View File

@@ -0,0 +1,10 @@
/**
* Generated by orval v8.2.0 🍺
* Do not edit manually.
* Mission Control API
* OpenAPI spec version: 0.1.0
*/
export type GetSessionHistoryApiV1GatewaySessionsSessionIdHistoryGetParams = {
board_id?: string | null;
};

View File

@@ -0,0 +1,11 @@
/**
* Generated by orval v8.2.0 🍺
* Do not edit manually.
* Mission Control API
* OpenAPI spec version: 0.1.0
*/
import type { ValidationError } from "./validationError";
export interface HTTPValidationError {
detail?: ValidationError[];
}

View File

@@ -0,0 +1,8 @@
/**
* Generated by orval v8.2.0 🍺
* Do not edit manually.
* Mission Control API
* OpenAPI spec version: 0.1.0
*/
export type HealthHealthGet200 = { [key: string]: boolean };

View File

@@ -0,0 +1,8 @@
/**
* Generated by orval v8.2.0 🍺
* Do not edit manually.
* Mission Control API
* OpenAPI spec version: 0.1.0
*/
export type HealthzHealthzGet200 = { [key: string]: boolean };

View File

@@ -0,0 +1,52 @@
/**
* Generated by orval v8.2.0 🍺
* Do not edit manually.
* Mission Control API
* OpenAPI spec version: 0.1.0
*/
export * from "./activityEventRead";
export * from "./agentCreate";
export * from "./agentCreateHeartbeatConfig";
export * from "./agentDeleteConfirm";
export * from "./agentHeartbeat";
export * from "./agentHeartbeatCreate";
export * from "./agentProvisionConfirm";
export * from "./agentRead";
export * from "./agentReadHeartbeatConfig";
export * from "./agentUpdate";
export * from "./agentUpdateHeartbeatConfig";
export * from "./boardCreate";
export * from "./boardRead";
export * from "./boardUpdate";
export * from "./confirmDeleteAgentApiV1AgentsAgentIdDeleteConfirmPost200";
export * from "./confirmProvisionAgentApiV1AgentsAgentIdProvisionConfirmPost200";
export * from "./deleteAgentApiV1AgentsAgentIdDelete200";
export * from "./deleteBoardApiV1BoardsBoardIdDelete200";
export * from "./deleteTaskApiV1BoardsBoardIdTasksTaskIdDelete200";
export * from "./gatewayCommandsApiV1GatewayCommandsGet200";
export * from "./gatewayStatusApiV1GatewayStatusGet200";
export * from "./gatewayStatusApiV1GatewayStatusGetParams";
export * from "./getGatewaySessionApiV1GatewaySessionsSessionIdGet200";
export * from "./getGatewaySessionApiV1GatewaySessionsSessionIdGetParams";
export * from "./getSessionHistoryApiV1GatewaySessionsSessionIdHistoryGet200";
export * from "./getSessionHistoryApiV1GatewaySessionsSessionIdHistoryGetParams";
export * from "./healthHealthGet200";
export * from "./healthzHealthzGet200";
export * from "./hTTPValidationError";
export * from "./listActivityApiV1ActivityGetParams";
export * from "./listSessionsApiV1GatewaySessionsGet200";
export * from "./listSessionsApiV1GatewaySessionsGetParams";
export * from "./listTasksApiV1BoardsBoardIdTasksGetParams";
export * from "./readyzReadyzGet200";
export * from "./sendSessionMessageApiV1GatewaySessionsSessionIdMessagePost200";
export * from "./sendSessionMessageApiV1GatewaySessionsSessionIdMessagePostBody";
export * from "./sendSessionMessageApiV1GatewaySessionsSessionIdMessagePostParams";
export * from "./taskCommentCreate";
export * from "./taskCommentRead";
export * from "./taskCreate";
export * from "./taskRead";
export * from "./taskUpdate";
export * from "./userRead";
export * from "./userUpdate";
export * from "./validationError";

View File

@@ -0,0 +1,18 @@
/**
* Generated by orval v8.2.0 🍺
* Do not edit manually.
* Mission Control API
* OpenAPI spec version: 0.1.0
*/
export type ListActivityApiV1ActivityGetParams = {
/**
* @minimum 1
* @maximum 200
*/
limit?: number;
/**
* @minimum 0
*/
offset?: number;
};

View File

@@ -0,0 +1,8 @@
/**
* Generated by orval v8.2.0 🍺
* Do not edit manually.
* Mission Control API
* OpenAPI spec version: 0.1.0
*/
export type ListSessionsApiV1GatewaySessionsGet200 = { [key: string]: unknown };

View File

@@ -0,0 +1,10 @@
/**
* Generated by orval v8.2.0 🍺
* Do not edit manually.
* Mission Control API
* OpenAPI spec version: 0.1.0
*/
export type ListSessionsApiV1GatewaySessionsGetParams = {
board_id?: string | null;
};

View File

@@ -0,0 +1,13 @@
/**
* Generated by orval v8.2.0 🍺
* Do not edit manually.
* Mission Control API
* OpenAPI spec version: 0.1.0
*/
export type ListTasksApiV1BoardsBoardIdTasksGetParams = {
status?: string | null;
assigned_agent_id?: string | null;
unassigned?: boolean | null;
limit?: number | null;
};

View File

@@ -0,0 +1,8 @@
/**
* Generated by orval v8.2.0 🍺
* Do not edit manually.
* Mission Control API
* OpenAPI spec version: 0.1.0
*/
export type ReadyzReadyzGet200 = { [key: string]: boolean };

View File

@@ -0,0 +1,10 @@
/**
* Generated by orval v8.2.0 🍺
* Do not edit manually.
* Mission Control API
* OpenAPI spec version: 0.1.0
*/
export type SendSessionMessageApiV1GatewaySessionsSessionIdMessagePost200 = {
[key: string]: boolean;
};

View File

@@ -0,0 +1,10 @@
/**
* Generated by orval v8.2.0 🍺
* Do not edit manually.
* Mission Control API
* OpenAPI spec version: 0.1.0
*/
export type SendSessionMessageApiV1GatewaySessionsSessionIdMessagePostBody = {
[key: string]: unknown;
};

View File

@@ -0,0 +1,10 @@
/**
* Generated by orval v8.2.0 🍺
* Do not edit manually.
* Mission Control API
* OpenAPI spec version: 0.1.0
*/
export type SendSessionMessageApiV1GatewaySessionsSessionIdMessagePostParams = {
board_id?: string | null;
};

View File

@@ -0,0 +1,10 @@
/**
* Generated by orval v8.2.0 🍺
* Do not edit manually.
* Mission Control API
* OpenAPI spec version: 0.1.0
*/
export interface TaskCommentCreate {
message: string;
}

View File

@@ -0,0 +1,14 @@
/**
* Generated by orval v8.2.0 🍺
* Do not edit manually.
* Mission Control API
* OpenAPI spec version: 0.1.0
*/
export interface TaskCommentRead {
id: string;
message: string | null;
agent_id: string | null;
task_id: string | null;
created_at: string;
}

View File

@@ -0,0 +1,16 @@
/**
* Generated by orval v8.2.0 🍺
* Do not edit manually.
* Mission Control API
* OpenAPI spec version: 0.1.0
*/
export interface TaskCreate {
title: string;
description?: string | null;
status?: string;
priority?: string;
due_at?: string | null;
assigned_agent_id?: string | null;
created_by_user_id?: string | null;
}

View File

@@ -0,0 +1,21 @@
/**
* Generated by orval v8.2.0 🍺
* Do not edit manually.
* Mission Control API
* OpenAPI spec version: 0.1.0
*/
export interface TaskRead {
title: string;
description?: string | null;
status?: string;
priority?: string;
due_at?: string | null;
assigned_agent_id?: string | null;
id: string;
board_id: string | null;
created_by_user_id: string | null;
in_progress_at: string | null;
created_at: string;
updated_at: string;
}

View File

@@ -0,0 +1,16 @@
/**
* Generated by orval v8.2.0 🍺
* Do not edit manually.
* Mission Control API
* OpenAPI spec version: 0.1.0
*/
export interface TaskUpdate {
title?: string | null;
description?: string | null;
status?: string | null;
priority?: string | null;
due_at?: string | null;
assigned_agent_id?: string | null;
comment?: string | null;
}

View File

@@ -0,0 +1,19 @@
/**
* Generated by orval v8.2.0 🍺
* Do not edit manually.
* Mission Control API
* OpenAPI spec version: 0.1.0
*/
export interface UserRead {
clerk_user_id: string;
email?: string | null;
name?: string | null;
preferred_name?: string | null;
pronouns?: string | null;
timezone?: string | null;
notes?: string | null;
context?: string | null;
id: string;
is_super_admin: boolean;
}

View File

@@ -0,0 +1,15 @@
/**
* Generated by orval v8.2.0 🍺
* Do not edit manually.
* Mission Control API
* OpenAPI spec version: 0.1.0
*/
export interface UserUpdate {
name?: string | null;
preferred_name?: string | null;
pronouns?: string | null;
timezone?: string | null;
notes?: string | null;
context?: string | null;
}

View File

@@ -0,0 +1,12 @@
/**
* Generated by orval v8.2.0 🍺
* Do not edit manually.
* Mission Control API
* OpenAPI spec version: 0.1.0
*/
export interface ValidationError {
loc: (string | number)[];
msg: string;
type: string;
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,314 @@
/**
* Generated by orval v8.2.0 🍺
* Do not edit manually.
* Mission Control API
* OpenAPI spec version: 0.1.0
*/
import { useMutation, useQuery } from "@tanstack/react-query";
import type {
DataTag,
DefinedInitialDataOptions,
DefinedUseQueryResult,
MutationFunction,
QueryClient,
QueryFunction,
QueryKey,
UndefinedInitialDataOptions,
UseMutationOptions,
UseMutationResult,
UseQueryOptions,
UseQueryResult,
} from "@tanstack/react-query";
import type { HTTPValidationError, UserRead, UserUpdate } from ".././model";
import { customFetch } from "../../mutator";
type SecondParameter<T extends (...args: never) => unknown> = Parameters<T>[1];
/**
* @summary Get Me
*/
export type getMeApiV1UsersMeGetResponse200 = {
data: UserRead;
status: 200;
};
export type getMeApiV1UsersMeGetResponseSuccess =
getMeApiV1UsersMeGetResponse200 & {
headers: Headers;
};
export type getMeApiV1UsersMeGetResponse = getMeApiV1UsersMeGetResponseSuccess;
export const getGetMeApiV1UsersMeGetUrl = () => {
return `/api/v1/users/me`;
};
export const getMeApiV1UsersMeGet = async (
options?: RequestInit,
): Promise<getMeApiV1UsersMeGetResponse> => {
return customFetch<getMeApiV1UsersMeGetResponse>(
getGetMeApiV1UsersMeGetUrl(),
{
...options,
method: "GET",
},
);
};
export const getGetMeApiV1UsersMeGetQueryKey = () => {
return [`/api/v1/users/me`] as const;
};
export const getGetMeApiV1UsersMeGetQueryOptions = <
TData = Awaited<ReturnType<typeof getMeApiV1UsersMeGet>>,
TError = unknown,
>(options?: {
query?: Partial<
UseQueryOptions<
Awaited<ReturnType<typeof getMeApiV1UsersMeGet>>,
TError,
TData
>
>;
request?: SecondParameter<typeof customFetch>;
}) => {
const { query: queryOptions, request: requestOptions } = options ?? {};
const queryKey = queryOptions?.queryKey ?? getGetMeApiV1UsersMeGetQueryKey();
const queryFn: QueryFunction<
Awaited<ReturnType<typeof getMeApiV1UsersMeGet>>
> = ({ signal }) => getMeApiV1UsersMeGet({ signal, ...requestOptions });
return { queryKey, queryFn, ...queryOptions } as UseQueryOptions<
Awaited<ReturnType<typeof getMeApiV1UsersMeGet>>,
TError,
TData
> & { queryKey: DataTag<QueryKey, TData, TError> };
};
export type GetMeApiV1UsersMeGetQueryResult = NonNullable<
Awaited<ReturnType<typeof getMeApiV1UsersMeGet>>
>;
export type GetMeApiV1UsersMeGetQueryError = unknown;
export function useGetMeApiV1UsersMeGet<
TData = Awaited<ReturnType<typeof getMeApiV1UsersMeGet>>,
TError = unknown,
>(
options: {
query: Partial<
UseQueryOptions<
Awaited<ReturnType<typeof getMeApiV1UsersMeGet>>,
TError,
TData
>
> &
Pick<
DefinedInitialDataOptions<
Awaited<ReturnType<typeof getMeApiV1UsersMeGet>>,
TError,
Awaited<ReturnType<typeof getMeApiV1UsersMeGet>>
>,
"initialData"
>;
request?: SecondParameter<typeof customFetch>;
},
queryClient?: QueryClient,
): DefinedUseQueryResult<TData, TError> & {
queryKey: DataTag<QueryKey, TData, TError>;
};
export function useGetMeApiV1UsersMeGet<
TData = Awaited<ReturnType<typeof getMeApiV1UsersMeGet>>,
TError = unknown,
>(
options?: {
query?: Partial<
UseQueryOptions<
Awaited<ReturnType<typeof getMeApiV1UsersMeGet>>,
TError,
TData
>
> &
Pick<
UndefinedInitialDataOptions<
Awaited<ReturnType<typeof getMeApiV1UsersMeGet>>,
TError,
Awaited<ReturnType<typeof getMeApiV1UsersMeGet>>
>,
"initialData"
>;
request?: SecondParameter<typeof customFetch>;
},
queryClient?: QueryClient,
): UseQueryResult<TData, TError> & {
queryKey: DataTag<QueryKey, TData, TError>;
};
export function useGetMeApiV1UsersMeGet<
TData = Awaited<ReturnType<typeof getMeApiV1UsersMeGet>>,
TError = unknown,
>(
options?: {
query?: Partial<
UseQueryOptions<
Awaited<ReturnType<typeof getMeApiV1UsersMeGet>>,
TError,
TData
>
>;
request?: SecondParameter<typeof customFetch>;
},
queryClient?: QueryClient,
): UseQueryResult<TData, TError> & {
queryKey: DataTag<QueryKey, TData, TError>;
};
/**
* @summary Get Me
*/
export function useGetMeApiV1UsersMeGet<
TData = Awaited<ReturnType<typeof getMeApiV1UsersMeGet>>,
TError = unknown,
>(
options?: {
query?: Partial<
UseQueryOptions<
Awaited<ReturnType<typeof getMeApiV1UsersMeGet>>,
TError,
TData
>
>;
request?: SecondParameter<typeof customFetch>;
},
queryClient?: QueryClient,
): UseQueryResult<TData, TError> & {
queryKey: DataTag<QueryKey, TData, TError>;
} {
const queryOptions = getGetMeApiV1UsersMeGetQueryOptions(options);
const query = useQuery(queryOptions, queryClient) as UseQueryResult<
TData,
TError
> & { queryKey: DataTag<QueryKey, TData, TError> };
return { ...query, queryKey: queryOptions.queryKey };
}
/**
* @summary Update Me
*/
export type updateMeApiV1UsersMePatchResponse200 = {
data: UserRead;
status: 200;
};
export type updateMeApiV1UsersMePatchResponse422 = {
data: HTTPValidationError;
status: 422;
};
export type updateMeApiV1UsersMePatchResponseSuccess =
updateMeApiV1UsersMePatchResponse200 & {
headers: Headers;
};
export type updateMeApiV1UsersMePatchResponseError =
updateMeApiV1UsersMePatchResponse422 & {
headers: Headers;
};
export type updateMeApiV1UsersMePatchResponse =
| updateMeApiV1UsersMePatchResponseSuccess
| updateMeApiV1UsersMePatchResponseError;
export const getUpdateMeApiV1UsersMePatchUrl = () => {
return `/api/v1/users/me`;
};
export const updateMeApiV1UsersMePatch = async (
userUpdate: UserUpdate,
options?: RequestInit,
): Promise<updateMeApiV1UsersMePatchResponse> => {
return customFetch<updateMeApiV1UsersMePatchResponse>(
getUpdateMeApiV1UsersMePatchUrl(),
{
...options,
method: "PATCH",
headers: { "Content-Type": "application/json", ...options?.headers },
body: JSON.stringify(userUpdate),
},
);
};
export const getUpdateMeApiV1UsersMePatchMutationOptions = <
TError = HTTPValidationError,
TContext = unknown,
>(options?: {
mutation?: UseMutationOptions<
Awaited<ReturnType<typeof updateMeApiV1UsersMePatch>>,
TError,
{ data: UserUpdate },
TContext
>;
request?: SecondParameter<typeof customFetch>;
}): UseMutationOptions<
Awaited<ReturnType<typeof updateMeApiV1UsersMePatch>>,
TError,
{ data: UserUpdate },
TContext
> => {
const mutationKey = ["updateMeApiV1UsersMePatch"];
const { mutation: mutationOptions, request: requestOptions } = options
? options.mutation &&
"mutationKey" in options.mutation &&
options.mutation.mutationKey
? options
: { ...options, mutation: { ...options.mutation, mutationKey } }
: { mutation: { mutationKey }, request: undefined };
const mutationFn: MutationFunction<
Awaited<ReturnType<typeof updateMeApiV1UsersMePatch>>,
{ data: UserUpdate }
> = (props) => {
const { data } = props ?? {};
return updateMeApiV1UsersMePatch(data, requestOptions);
};
return { mutationFn, ...mutationOptions };
};
export type UpdateMeApiV1UsersMePatchMutationResult = NonNullable<
Awaited<ReturnType<typeof updateMeApiV1UsersMePatch>>
>;
export type UpdateMeApiV1UsersMePatchMutationBody = UserUpdate;
export type UpdateMeApiV1UsersMePatchMutationError = HTTPValidationError;
/**
* @summary Update Me
*/
export const useUpdateMeApiV1UsersMePatch = <
TError = HTTPValidationError,
TContext = unknown,
>(
options?: {
mutation?: UseMutationOptions<
Awaited<ReturnType<typeof updateMeApiV1UsersMePatch>>,
TError,
{ data: UserUpdate },
TContext
>;
request?: SecondParameter<typeof customFetch>;
},
queryClient?: QueryClient,
): UseMutationResult<
Awaited<ReturnType<typeof updateMeApiV1UsersMePatch>>,
TError,
{ data: UserUpdate },
TContext
> => {
return useMutation(
getUpdateMeApiV1UsersMePatchMutationOptions(options),
queryClient,
);
};

View File

@@ -2,7 +2,11 @@ export const customFetch = async <T>(
url: string,
options: RequestInit
): Promise<T> => {
const baseUrl = process.env.NEXT_PUBLIC_API_URL ?? "http://localhost:8000";
const rawBaseUrl = process.env.NEXT_PUBLIC_API_URL;
if (!rawBaseUrl) {
throw new Error("NEXT_PUBLIC_API_URL is not set.");
}
const baseUrl = rawBaseUrl.replace(/\/+$/, "");
const response = await fetch(`${baseUrl}${url}`, {
...options,
headers: {

View File

@@ -9,6 +9,7 @@ import { DashboardSidebar } from "@/components/organisms/DashboardSidebar";
import { DashboardShell } from "@/components/templates/DashboardShell";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { getApiBaseUrl } from "@/lib/api-base";
import {
Select,
SelectContent,
@@ -17,9 +18,7 @@ import {
SelectValue,
} from "@/components/ui/select";
const apiBase =
process.env.NEXT_PUBLIC_API_URL?.replace(/\/+$/, "") ||
"http://localhost:8000";
const apiBase = getApiBaseUrl();
type Agent = {
id: string;

View File

@@ -18,10 +18,9 @@ import {
DialogHeader,
DialogTitle,
} from "@/components/ui/dialog";
import { getApiBaseUrl } from "@/lib/api-base";
const apiBase =
process.env.NEXT_PUBLIC_API_URL?.replace(/\/+$/, "") ||
"http://localhost:8000";
const apiBase = getApiBaseUrl();
type Agent = {
id: string;

View File

@@ -9,6 +9,7 @@ import { DashboardSidebar } from "@/components/organisms/DashboardSidebar";
import { DashboardShell } from "@/components/templates/DashboardShell";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { getApiBaseUrl } from "@/lib/api-base";
import {
Select,
SelectContent,
@@ -17,9 +18,7 @@ import {
SelectValue,
} from "@/components/ui/select";
const apiBase =
process.env.NEXT_PUBLIC_API_URL?.replace(/\/+$/, "") ||
"http://localhost:8000";
const apiBase = getApiBaseUrl();
type Agent = {
id: string;

View File

@@ -33,10 +33,9 @@ import {
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import { getApiBaseUrl } from "@/lib/api-base";
const apiBase =
process.env.NEXT_PUBLIC_API_URL?.replace(/\/+$/, "") ||
"http://localhost:8000";
const apiBase = getApiBaseUrl();
type Agent = {
id: string;

View File

@@ -9,10 +9,10 @@ import { DashboardSidebar } from "@/components/organisms/DashboardSidebar";
import { DashboardShell } from "@/components/templates/DashboardShell";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Textarea } from "@/components/ui/textarea";
import { getApiBaseUrl } from "@/lib/api-base";
const apiBase =
process.env.NEXT_PUBLIC_API_URL?.replace(/\/+$/, "") ||
"http://localhost:8000";
const apiBase = getApiBaseUrl();
type Board = {
id: string;
@@ -21,6 +21,8 @@ type Board = {
gateway_url?: string | null;
gateway_main_session_key?: string | null;
gateway_workspace_root?: string | null;
identity_template?: string | null;
soul_template?: string | null;
};
const slugify = (value: string) =>
@@ -44,6 +46,8 @@ export default function EditBoardPage() {
const [gatewayToken, setGatewayToken] = useState("");
const [gatewayMainSessionKey, setGatewayMainSessionKey] = useState("");
const [gatewayWorkspaceRoot, setGatewayWorkspaceRoot] = useState("");
const [identityTemplate, setIdentityTemplate] = useState("");
const [soulTemplate, setSoulTemplate] = useState("");
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
@@ -66,6 +70,8 @@ export default function EditBoardPage() {
setGatewayUrl(data.gateway_url ?? "");
setGatewayMainSessionKey(data.gateway_main_session_key ?? "");
setGatewayWorkspaceRoot(data.gateway_workspace_root ?? "");
setIdentityTemplate(data.identity_template ?? "");
setSoulTemplate(data.soul_template ?? "");
} catch (err) {
setError(err instanceof Error ? err.message : "Something went wrong.");
} finally {
@@ -96,6 +102,8 @@ export default function EditBoardPage() {
gateway_url: gatewayUrl.trim() || null,
gateway_main_session_key: gatewayMainSessionKey.trim() || null,
gateway_workspace_root: gatewayWorkspaceRoot.trim() || null,
identity_template: identityTemplate.trim() || null,
soul_template: soulTemplate.trim() || null,
};
if (gatewayToken.trim()) {
payload.gateway_token = gatewayToken.trim();
@@ -210,6 +218,28 @@ export default function EditBoardPage() {
disabled={isLoading}
/>
</div>
<div className="space-y-2">
<label className="text-sm font-medium text-strong">
Identity template (optional)
</label>
<Textarea
value={identityTemplate}
onChange={(event) => setIdentityTemplate(event.target.value)}
placeholder="Override IDENTITY.md for agents in this board."
className="min-h-[140px]"
/>
</div>
<div className="space-y-2">
<label className="text-sm font-medium text-strong">
Soul template (optional)
</label>
<Textarea
value={soulTemplate}
onChange={(event) => setSoulTemplate(event.target.value)}
placeholder="Override SOUL.md for agents in this board."
className="min-h-[160px]"
/>
</div>
{error ? (
<div className="rounded-lg border border-[color:var(--border)] bg-[color:var(--surface-muted)] p-3 text-xs text-muted">
{error}

View File

@@ -26,6 +26,7 @@ import {
SelectValue,
} from "@/components/ui/select";
import { Textarea } from "@/components/ui/textarea";
import { getApiBaseUrl } from "@/lib/api-base";
type Board = {
id: string;
@@ -57,9 +58,7 @@ type TaskComment = {
created_at: string;
};
const apiBase =
process.env.NEXT_PUBLIC_API_URL?.replace(/\/+$/, "") ||
"http://localhost:8000";
const apiBase = getApiBaseUrl();
const priorities = [
{ value: "low", label: "Low" },

View File

@@ -9,6 +9,8 @@ import { DashboardSidebar } from "@/components/organisms/DashboardSidebar";
import { DashboardShell } from "@/components/templates/DashboardShell";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Textarea } from "@/components/ui/textarea";
import { getApiBaseUrl } from "@/lib/api-base";
type Board = {
id: string;
@@ -18,11 +20,11 @@ type Board = {
gateway_token?: string | null;
gateway_main_session_key?: string | null;
gateway_workspace_root?: string | null;
identity_template?: string | null;
soul_template?: string | null;
};
const apiBase =
process.env.NEXT_PUBLIC_API_URL?.replace(/\/+$/, "") ||
"http://localhost:8000";
const apiBase = getApiBaseUrl();
const slugify = (value: string) =>
value
@@ -39,6 +41,8 @@ export default function NewBoardPage() {
const [gatewayToken, setGatewayToken] = useState("");
const [gatewayMainSessionKey, setGatewayMainSessionKey] = useState("");
const [gatewayWorkspaceRoot, setGatewayWorkspaceRoot] = useState("");
const [identityTemplate, setIdentityTemplate] = useState("");
const [soulTemplate, setSoulTemplate] = useState("");
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
@@ -63,6 +67,12 @@ export default function NewBoardPage() {
if (gatewayWorkspaceRoot.trim()) {
payload.gateway_workspace_root = gatewayWorkspaceRoot.trim();
}
if (identityTemplate.trim()) {
payload.identity_template = identityTemplate.trim();
}
if (soulTemplate.trim()) {
payload.soul_template = soulTemplate.trim();
}
const response = await fetch(`${apiBase}/api/v1/boards`, {
method: "POST",
headers: {
@@ -170,6 +180,28 @@ export default function NewBoardPage() {
disabled={isLoading}
/>
</div>
<div className="space-y-2">
<label className="text-sm font-medium text-strong">
Identity template (optional)
</label>
<Textarea
value={identityTemplate}
onChange={(event) => setIdentityTemplate(event.target.value)}
placeholder="Override IDENTITY.md for agents in this board."
className="min-h-[140px]"
/>
</div>
<div className="space-y-2">
<label className="text-sm font-medium text-strong">
Soul template (optional)
</label>
<Textarea
value={soulTemplate}
onChange={(event) => setSoulTemplate(event.target.value)}
placeholder="Override SOUL.md for agents in this board."
className="min-h-[160px]"
/>
</div>
{error ? (
<div className="rounded-lg border border-[color:var(--border)] bg-[color:var(--surface-muted)] p-3 text-xs text-muted">
{error}

View File

@@ -15,6 +15,7 @@ import {
import { DashboardSidebar } from "@/components/organisms/DashboardSidebar";
import { DashboardShell } from "@/components/templates/DashboardShell";
import { Button } from "@/components/ui/button";
import { getApiBaseUrl } from "@/lib/api-base";
import {
Dialog,
DialogContent,
@@ -30,9 +31,7 @@ type Board = {
slug: string;
};
const apiBase =
process.env.NEXT_PUBLIC_API_URL?.replace(/\/+$/, "") ||
"http://localhost:8000";
const apiBase = getApiBaseUrl();
export default function BoardsPage() {
const { getToken, isSignedIn } = useAuth();

View File

@@ -20,8 +20,8 @@ export default function DashboardPage() {
</p>
<SignInButton
mode="modal"
forceRedirectUrl="/boards"
signUpForceRedirectUrl="/boards"
forceRedirectUrl="/onboarding"
signUpForceRedirectUrl="/onboarding"
>
<Button>Sign in</Button>
</SignInButton>

View File

@@ -0,0 +1,275 @@
"use client";
import { useEffect, useMemo, useState } from "react";
import { useRouter } from "next/navigation";
import { SignInButton, SignedIn, SignedOut, useAuth, useUser } from "@clerk/nextjs";
import { Globe, Info, RotateCcw, Save, User } from "lucide-react";
import { DashboardShell } from "@/components/templates/DashboardShell";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import SearchableSelect from "@/components/ui/searchable-select";
import { getApiBaseUrl } from "@/lib/api-base";
const apiBase = getApiBaseUrl();
type UserProfile = {
id: string;
name?: string | null;
preferred_name?: string | null;
pronouns?: string | null;
timezone?: string | null;
notes?: string | null;
context?: string | null;
};
const isCompleteProfile = (profile: UserProfile | null) => {
if (!profile) return false;
const resolvedName = profile.preferred_name?.trim() || profile.name?.trim();
return Boolean(resolvedName) && Boolean(profile.timezone?.trim());
};
export default function OnboardingPage() {
const router = useRouter();
const { getToken, isSignedIn } = useAuth();
const { user } = useUser();
const [profile, setProfile] = useState<UserProfile | null>(null);
const [name, setName] = useState("");
const [timezone, setTimezone] = useState("");
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const requiredMissing = useMemo(
() => [name, timezone].some((value) => !value.trim()),
[name, timezone]
);
const timezones = useMemo(() => {
if (typeof Intl !== "undefined" && "supportedValuesOf" in Intl) {
return (Intl as typeof Intl & { supportedValuesOf: (key: string) => string[] })
.supportedValuesOf("timeZone")
.sort();
}
return [
"America/Los_Angeles",
"America/Denver",
"America/Chicago",
"America/New_York",
"America/Sao_Paulo",
"Europe/London",
"Europe/Berlin",
"Europe/Paris",
"Asia/Dubai",
"Asia/Kolkata",
"Asia/Singapore",
"Asia/Tokyo",
"Australia/Sydney",
];
}, []);
const timezoneOptions = useMemo(
() => timezones.map((tz) => ({ value: tz, label: tz })),
[timezones],
);
const loadProfile = async () => {
if (!isSignedIn) return;
setIsLoading(true);
setError(null);
try {
const token = await getToken();
const response = await fetch(`${apiBase}/api/v1/users/me`, {
headers: { Authorization: token ? `Bearer ${token}` : "" },
});
if (!response.ok) {
throw new Error("Unable to load profile.");
}
const data = (await response.json()) as UserProfile;
setProfile(data);
const fallbackName =
user?.fullName ?? user?.firstName ?? user?.username ?? "";
setName(data.preferred_name ?? data.name ?? fallbackName);
setTimezone(data.timezone ?? "");
if (isCompleteProfile(data)) {
router.replace("/boards");
}
} catch (err) {
setError(err instanceof Error ? err.message : "Something went wrong.");
} finally {
setIsLoading(false);
}
};
useEffect(() => {
if (!name.trim() && user) {
const fallbackName =
user.fullName ?? user.firstName ?? user.username ?? "";
if (fallbackName) {
setName(fallbackName);
}
}
}, [user, name]);
useEffect(() => {
loadProfile();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isSignedIn]);
const handleSubmit = async (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
if (!isSignedIn) return;
if (requiredMissing) {
setError("Please complete the required fields.");
return;
}
setIsLoading(true);
setError(null);
try {
const token = await getToken();
const normalizedName = name.trim();
const payload = {
name: normalizedName,
preferred_name: normalizedName,
timezone: timezone.trim(),
};
const response = await fetch(`${apiBase}/api/v1/users/me`, {
method: "PATCH",
headers: {
"Content-Type": "application/json",
Authorization: token ? `Bearer ${token}` : "",
},
body: JSON.stringify(payload),
});
if (!response.ok) {
throw new Error("Unable to update profile.");
}
router.replace("/boards");
} catch (err) {
setError(err instanceof Error ? err.message : "Something went wrong.");
} finally {
setIsLoading(false);
}
};
return (
<DashboardShell>
<SignedOut>
<div className="lg:col-span-2 flex min-h-[70vh] items-center justify-center">
<div className="w-full max-w-2xl rounded-xl border border-slate-200 bg-white shadow-sm">
<div className="border-b border-slate-100 px-6 py-5">
<h1 className="text-2xl font-semibold tracking-tight text-slate-900">
Mission Control profile
</h1>
<p className="mt-1 text-sm text-slate-600">
Sign in to configure your profile and timezone.
</p>
</div>
<div className="px-6 py-6">
<SignInButton
mode="modal"
forceRedirectUrl="/onboarding"
signUpForceRedirectUrl="/onboarding"
>
<Button size="lg">Sign in</Button>
</SignInButton>
</div>
</div>
</div>
</SignedOut>
<SignedIn>
<div className="lg:col-span-2 flex min-h-[70vh] items-center justify-center">
<section className="w-full max-w-2xl rounded-xl border border-slate-200 bg-white shadow-sm">
<div className="border-b border-slate-100 px-6 py-5">
<h1 className="text-2xl font-semibold tracking-tight text-slate-900">
Mission Control profile
</h1>
<p className="mt-1 text-sm text-slate-600">
Configure your mission control settings and preferences.
</p>
</div>
<div className="px-6 py-6">
<form onSubmit={handleSubmit} className="space-y-6">
<div className="grid gap-6 md:grid-cols-2">
<div className="space-y-2">
<label className="text-sm font-medium text-slate-700 flex items-center gap-2">
<User className="h-4 w-4 text-slate-500" />
Name
<span className="text-red-500">*</span>
</label>
<Input
value={name}
onChange={(event) => setName(event.target.value)}
placeholder="Enter your name"
disabled={isLoading}
className="border-slate-300 text-slate-900 focus-visible:ring-blue-500"
/>
</div>
<div className="space-y-2">
<label className="text-sm font-medium text-slate-700 flex items-center gap-2">
<Globe className="h-4 w-4 text-slate-500" />
Timezone
<span className="text-red-500">*</span>
</label>
<SearchableSelect
ariaLabel="Select timezone"
value={timezone}
onValueChange={setTimezone}
options={timezoneOptions}
placeholder="Select timezone"
searchPlaceholder="Search timezones..."
emptyMessage="No matching timezones."
triggerClassName="w-full h-11 rounded-xl border border-slate-300 bg-white px-3 py-2 text-sm font-medium text-slate-900 shadow-sm focus:border-blue-500 focus:ring-2 focus:ring-blue-200"
contentClassName="rounded-xl border border-slate-200 shadow-lg"
itemClassName="px-4 py-3 text-sm text-slate-700 data-[selected=true]:bg-slate-50 data-[selected=true]:text-slate-900"
/>
</div>
</div>
<div className="rounded-lg border border-blue-200 bg-blue-50 p-4 text-sm text-blue-800 flex items-start gap-3">
<Info className="mt-0.5 h-4 w-4 text-blue-600" />
<p>
<strong>Note:</strong> Your timezone is used to display all
timestamps and schedule mission-critical events accurately.
</p>
</div>
{error ? (
<div className="rounded-lg border border-slate-200 bg-slate-50 p-3 text-xs text-slate-600">
{error}
</div>
) : null}
<div className="flex flex-wrap gap-3 pt-2">
<Button
type="submit"
className="flex-1 bg-blue-600 text-white hover:bg-blue-700 py-2.5"
disabled={isLoading || requiredMissing}
>
<Save className="h-4 w-4" />
{isLoading ? "Saving…" : "Save Profile"}
</Button>
<button
type="button"
onClick={() => {
setName("");
setTimezone("");
setError(null);
}}
className="flex-1 rounded-md border border-slate-300 px-4 py-2.5 text-sm font-medium text-slate-700 transition-colors hover:bg-slate-50"
>
<span className="inline-flex items-center gap-2">
<RotateCcw className="h-4 w-4" />
Reset
</span>
</button>
</div>
</form>
</div>
</section>
</div>
</SignedIn>
</DashboardShell>
);
}

View File

@@ -14,8 +14,8 @@ export function LandingHero() {
<SignedOut>
<SignInButton
mode="modal"
forceRedirectUrl="/boards"
signUpForceRedirectUrl="/boards"
forceRedirectUrl="/onboarding"
signUpForceRedirectUrl="/onboarding"
>
<Button size="lg" className="w-full sm:w-auto">
Sign in to open mission control

View File

@@ -0,0 +1,91 @@
"use client";
import Image from "next/image";
import { SignOutButton, useUser } from "@clerk/nextjs";
import { LogOut } from "lucide-react";
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
import { cn } from "@/lib/utils";
export function UserMenu({ className }: { className?: string }) {
const { user } = useUser();
if (!user) return null;
const avatarUrl = user.imageUrl ?? null;
const avatarLabelSource = user.firstName ?? user.username ?? user.id ?? "U";
const avatarLabel = avatarLabelSource.slice(0, 1).toUpperCase();
const displayName =
user.fullName ?? user.firstName ?? user.username ?? "Account";
const displayEmail = user.primaryEmailAddress?.emailAddress ?? "";
return (
<Popover>
<PopoverTrigger asChild>
<button
type="button"
className={cn(
"flex h-12 items-center gap-2 rounded-lg px-2.5 text-gray-900 transition hover:bg-gray-100",
className,
)}
aria-label="Open user menu"
>
<span className="flex h-10 w-10 items-center justify-center overflow-hidden rounded-full bg-gray-100 text-sm font-semibold text-gray-900">
{avatarUrl ? (
<Image
src={avatarUrl}
alt="User avatar"
width={40}
height={40}
className="h-10 w-10 object-cover"
/>
) : (
avatarLabel
)}
</span>
</button>
</PopoverTrigger>
<PopoverContent
align="end"
sideOffset={8}
className="w-64 rounded-2xl border border-[color:var(--border)] bg-[color:var(--surface)] p-0 shadow-lg"
>
<div className="border-b border-[color:var(--border)] px-4 py-3">
<div className="flex items-center gap-3">
<span className="flex h-10 w-10 items-center justify-center overflow-hidden rounded-full bg-[color:var(--surface-muted)] text-sm font-semibold text-strong">
{avatarUrl ? (
<Image
src={avatarUrl}
alt="User avatar"
width={40}
height={40}
className="h-10 w-10 object-cover"
/>
) : (
avatarLabel
)}
</span>
<div className="min-w-0">
<div className="truncate text-sm font-semibold text-strong">
{displayName}
</div>
{displayEmail ? (
<div className="truncate text-xs text-muted">{displayEmail}</div>
) : null}
</div>
</div>
</div>
<div className="p-2">
<SignOutButton>
<button
type="button"
className="flex w-full items-center gap-2 rounded-xl px-3 py-2 text-sm font-semibold text-strong transition hover:bg-[color:var(--surface-strong)]"
>
<LogOut className="h-4 w-4 text-[color:var(--text-quiet)]" />
Sign out
</button>
</SignOutButton>
</div>
</PopoverContent>
</Popover>
);
}

View File

@@ -2,9 +2,10 @@
import type { ReactNode } from "react";
import { SignedIn, UserButton } from "@clerk/nextjs";
import { SignedIn } from "@clerk/nextjs";
import { BrandMark } from "@/components/atoms/BrandMark";
import { UserMenu } from "@/components/organisms/UserMenu";
export function DashboardShell({ children }: { children: ReactNode }) {
return (
@@ -14,12 +15,10 @@ export function DashboardShell({ children }: { children: ReactNode }) {
aria-hidden="true"
/>
<div className="relative flex min-h-screen w-full flex-col">
<header className="flex flex-wrap items-center justify-between gap-4 border-b border-[color:var(--border)] bg-[color:rgba(244,246,250,0.8)] px-6 py-5 backdrop-blur">
<header className="flex flex-wrap items-center justify-between gap-4 border-b border-[color:var(--border)] bg-[color:rgba(244,246,250,0.8)] px-6 py-3 backdrop-blur">
<BrandMark />
<SignedIn>
<div className="rounded-full border border-[color:var(--border)] bg-[color:var(--surface)] px-2 py-1 shadow-sm">
<UserButton />
</div>
<UserMenu />
</SignedIn>
</header>
<div className="flex-1 px-6 py-6">

View File

@@ -2,9 +2,10 @@
import type { ReactNode } from "react";
import { SignedIn, UserButton } from "@clerk/nextjs";
import { SignedIn } from "@clerk/nextjs";
import { BrandMark } from "@/components/atoms/BrandMark";
import { UserMenu } from "@/components/organisms/UserMenu";
export function LandingShell({ children }: { children: ReactNode }) {
return (
@@ -27,9 +28,7 @@ export function LandingShell({ children }: { children: ReactNode }) {
<header className="flex items-center justify-between gap-4">
<BrandMark />
<SignedIn>
<div className="rounded-full border border-[color:var(--border)] bg-[color:var(--surface)] px-2 py-1 shadow-sm">
<UserButton />
</div>
<UserMenu />
</SignedIn>
</header>
<main>{children}</main>

View File

@@ -0,0 +1,133 @@
"use client";
import * as React from "react";
import { Command as CommandPrimitive } from "cmdk";
import { Search } from "lucide-react";
import { cn } from "@/lib/utils";
const Command = React.forwardRef<
React.ElementRef<typeof CommandPrimitive>,
React.ComponentPropsWithoutRef<typeof CommandPrimitive>
>(({ className, ...props }, ref) => (
<CommandPrimitive
ref={ref}
className={cn(
"flex h-full w-full flex-col overflow-hidden rounded-md bg-popover text-popover-foreground",
className,
)}
{...props}
/>
));
Command.displayName = CommandPrimitive.displayName;
const CommandInput = React.forwardRef<
React.ElementRef<typeof CommandPrimitive.Input>,
React.ComponentPropsWithoutRef<typeof CommandPrimitive.Input>
>(({ className, ...props }, ref) => (
<div className="border-b border-slate-200 p-2" cmdk-input-wrapper="">
<div className="relative">
<Search className="absolute left-2 top-2.5 h-4 w-4 text-slate-400" />
<CommandPrimitive.Input
ref={ref}
className={cn(
"w-full rounded-md border border-slate-300 bg-white py-2 pl-8 pr-3 text-sm text-slate-900 placeholder:text-slate-500 focus:border-transparent focus:outline-none focus:ring-2 focus:ring-slate-400 disabled:cursor-not-allowed disabled:opacity-50",
className,
)}
{...props}
/>
</div>
</div>
));
CommandInput.displayName = CommandPrimitive.Input.displayName;
const CommandList = React.forwardRef<
React.ElementRef<typeof CommandPrimitive.List>,
React.ComponentPropsWithoutRef<typeof CommandPrimitive.List>
>(({ className, ...props }, ref) => (
<CommandPrimitive.List
ref={ref}
className={cn("max-h-[300px] overflow-y-auto overflow-x-hidden", className)}
{...props}
/>
));
CommandList.displayName = CommandPrimitive.List.displayName;
const CommandEmpty = React.forwardRef<
React.ElementRef<typeof CommandPrimitive.Empty>,
React.ComponentPropsWithoutRef<typeof CommandPrimitive.Empty>
>((props, ref) => (
<CommandPrimitive.Empty
ref={ref}
className="py-6 text-center text-sm"
{...props}
/>
));
CommandEmpty.displayName = CommandPrimitive.Empty.displayName;
const CommandGroup = React.forwardRef<
React.ElementRef<typeof CommandPrimitive.Group>,
React.ComponentPropsWithoutRef<typeof CommandPrimitive.Group>
>(({ className, ...props }, ref) => (
<CommandPrimitive.Group
ref={ref}
className={cn(
"overflow-hidden p-1 text-foreground [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-semibold [&_[cmdk-group-heading]]:text-muted-foreground",
className,
)}
{...props}
/>
));
CommandGroup.displayName = CommandPrimitive.Group.displayName;
const CommandSeparator = React.forwardRef<
React.ElementRef<typeof CommandPrimitive.Separator>,
React.ComponentPropsWithoutRef<typeof CommandPrimitive.Separator>
>(({ className, ...props }, ref) => (
<CommandPrimitive.Separator
ref={ref}
className={cn("-mx-1 h-px bg-muted", className)}
{...props}
/>
));
CommandSeparator.displayName = CommandPrimitive.Separator.displayName;
const CommandItem = React.forwardRef<
React.ElementRef<typeof CommandPrimitive.Item>,
React.ComponentPropsWithoutRef<typeof CommandPrimitive.Item>
>(({ className, ...props }, ref) => (
<CommandPrimitive.Item
ref={ref}
className={cn(
"relative flex cursor-pointer select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none data-[disabled=true]:pointer-events-none data-[selected=true]:bg-accent data-[selected=true]:text-accent-foreground data-[disabled=true]:opacity-50 data-[disabled=true]:cursor-not-allowed",
className,
)}
{...props}
/>
));
CommandItem.displayName = CommandPrimitive.Item.displayName;
const CommandShortcut = ({
className,
...props
}: React.HTMLAttributes<HTMLSpanElement>) => (
<span
className={cn(
"ml-auto text-xs tracking-widest text-muted-foreground",
className,
)}
{...props}
/>
);
CommandShortcut.displayName = "CommandShortcut";
export {
Command,
CommandInput,
CommandList,
CommandEmpty,
CommandGroup,
CommandItem,
CommandSeparator,
CommandShortcut,
};

View File

@@ -0,0 +1,215 @@
"use client";
import * as React from "react";
import { Check, ChevronDown } from "lucide-react";
import { cn } from "@/lib/utils";
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
import {
Command,
CommandEmpty,
CommandInput,
CommandItem,
CommandList,
} from "@/components/ui/command";
export type DropdownSelectOption = {
value: string;
label: string;
disabled?: boolean;
icon?: React.ElementType<{ className?: string }>;
iconClassName?: string;
};
type DropdownSelectProps = {
value?: string;
onValueChange: (value: string) => void;
options: DropdownSelectOption[];
placeholder?: string;
ariaLabel: string;
disabled?: boolean;
triggerClassName?: string;
contentClassName?: string;
itemClassName?: string;
searchEnabled?: boolean;
searchPlaceholder?: string;
emptyMessage?: string;
};
const resolvePlaceholder = (ariaLabel: string, placeholder?: string) => {
if (placeholder) {
return placeholder;
}
const trimmed = ariaLabel.trim();
if (!trimmed) {
return "Select an option";
}
return trimmed.endsWith("...") ? trimmed : `${trimmed}...`;
};
const resolveSearchPlaceholder = (
ariaLabel: string,
searchPlaceholder?: string,
) => {
if (searchPlaceholder) {
return searchPlaceholder;
}
const cleaned = ariaLabel.replace(/^select\\s+/i, "").trim();
if (!cleaned) {
return "Search...";
}
const value = `Search ${cleaned}`;
return value.endsWith("...") ? value : `${value}...`;
};
export default function DropdownSelect({
value,
onValueChange,
options,
placeholder,
ariaLabel,
disabled = false,
triggerClassName,
contentClassName,
itemClassName,
searchEnabled = true,
searchPlaceholder,
emptyMessage,
}: DropdownSelectProps) {
const [open, setOpen] = React.useState(false);
const [searchValue, setSearchValue] = React.useState("");
const listRef = React.useRef<HTMLDivElement | null>(null);
const selectedOption = options.find((option) => option.value === value);
const resolvedPlaceholder = resolvePlaceholder(ariaLabel, placeholder);
const triggerLabel = selectedOption?.label ?? value ?? "";
const SelectedIcon = selectedOption?.icon;
const selectedIconClassName = selectedOption?.iconClassName;
const showPlaceholder = !triggerLabel;
const resolvedSearchPlaceholder = searchEnabled
? resolveSearchPlaceholder(ariaLabel, searchPlaceholder)
: "";
const handleOpenChange = (nextOpen: boolean) => {
setOpen(nextOpen);
if (!nextOpen) {
setSearchValue("");
}
};
const handleSelect = (nextValue: string) => {
if (nextValue !== value) {
onValueChange(nextValue);
}
handleOpenChange(false);
};
React.useEffect(() => {
if (!open) {
return;
}
if (listRef.current) {
listRef.current.scrollTop = 0;
}
}, [open, searchValue]);
return (
<Popover open={open} onOpenChange={handleOpenChange}>
<PopoverTrigger asChild>
<button
type="button"
aria-label={ariaLabel}
aria-expanded={open}
aria-haspopup="listbox"
disabled={disabled}
className={cn(
"inline-flex h-10 w-auto cursor-pointer items-center justify-between gap-2 rounded-md border border-slate-300 bg-white px-3 py-2 text-sm font-medium text-slate-900 shadow-sm transition-colors hover:bg-slate-50 focus:outline-none focus:ring-2 focus:ring-slate-400 focus:border-transparent disabled:cursor-not-allowed disabled:opacity-50",
open && "bg-slate-50",
triggerClassName,
)}
>
<span
className={cn(
"flex min-w-0 items-center gap-2 truncate",
showPlaceholder && "text-slate-500",
)}
>
{SelectedIcon ? (
<SelectedIcon
className={cn("h-4 w-4 text-slate-600", selectedIconClassName)}
/>
) : null}
<span className="truncate">
{showPlaceholder ? resolvedPlaceholder : triggerLabel}
</span>
</span>
<ChevronDown
className={cn(
"h-4 w-4 shrink-0 text-slate-400 transition-transform",
open && "rotate-180",
)}
/>
</button>
</PopoverTrigger>
<PopoverContent
align="start"
sideOffset={6}
className={cn(
"w-[var(--radix-popover-trigger-width)] min-w-[12rem] overflow-hidden rounded-md border border-slate-200 bg-white p-0 text-slate-900 shadow-lg",
contentClassName,
)}
>
<Command label={ariaLabel} className="w-full">
{searchEnabled ? (
<CommandInput
value={searchValue}
onValueChange={setSearchValue}
placeholder={resolvedSearchPlaceholder}
autoFocus
className="text-sm"
/>
) : null}
<CommandList ref={listRef} className="max-h-64 p-1">
<CommandEmpty className="px-3 py-6 text-center text-sm text-slate-500">
{emptyMessage ?? "No results found."}
</CommandEmpty>
{options.map((option) => {
const isSelected = value === option.value;
const OptionIcon = option.icon;
return (
<CommandItem
key={option.value}
value={option.value}
keywords={[option.label, option.value]}
disabled={option.disabled}
onSelect={handleSelect}
className={cn(
"flex items-center justify-between gap-2 rounded-lg px-4 py-3 text-sm text-gray-700 transition-colors data-[selected=true]:bg-gray-50 data-[selected=true]:text-gray-900",
isSelected && "font-semibold",
!isSelected && "hover:bg-gray-50",
itemClassName,
)}
>
<span className="flex min-w-0 items-center gap-2">
{OptionIcon ? (
<OptionIcon
className={cn(
"h-4 w-4",
isSelected ? "text-gray-700" : "text-gray-500",
option.iconClassName,
)}
/>
) : null}
<span className="truncate font-medium">{option.label}</span>
</span>
{isSelected ? (
<Check className="h-4 w-4 text-gray-400" />
) : null}
</CommandItem>
);
})}
</CommandList>
</Command>
</PopoverContent>
</Popover>
);
}

View File

@@ -0,0 +1,31 @@
"use client";
import * as React from "react";
import * as PopoverPrimitive from "@radix-ui/react-popover";
import { cn } from "@/lib/utils";
const Popover = PopoverPrimitive.Root;
const PopoverTrigger = PopoverPrimitive.Trigger;
const PopoverAnchor = PopoverPrimitive.Anchor;
const PopoverContent = React.forwardRef<
React.ElementRef<typeof PopoverPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof PopoverPrimitive.Content>
>(({ className, align = "center", sideOffset = 4, ...props }, ref) => (
<PopoverPrimitive.Portal>
<PopoverPrimitive.Content
ref={ref}
align={align}
sideOffset={sideOffset}
className={cn(
"z-50 w-72 rounded-md border border-popover bg-popover p-4 text-popover-foreground shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
className,
)}
{...props}
/>
</PopoverPrimitive.Portal>
));
PopoverContent.displayName = PopoverPrimitive.Content.displayName;
export { Popover, PopoverTrigger, PopoverContent, PopoverAnchor };

View File

@@ -0,0 +1,62 @@
"use client";
import DropdownSelect, {
type DropdownSelectOption,
} from "@/components/ui/dropdown-select";
import { cn } from "@/lib/utils";
export type SearchableSelectOption = DropdownSelectOption;
type SearchableSelectProps = {
value?: string;
onValueChange: (value: string) => void;
options: SearchableSelectOption[];
placeholder?: string;
ariaLabel: string;
disabled?: boolean;
triggerClassName?: string;
contentClassName?: string;
itemClassName?: string;
searchEnabled?: boolean;
searchPlaceholder?: string;
emptyMessage?: string;
};
const baseTriggerClassName =
"w-auto h-auto rounded-xl border-2 border-gray-200 bg-white px-4 py-3 text-left text-sm font-semibold text-gray-700 shadow-sm transition-all duration-200 hover:border-gray-300 focus:border-gray-900 focus:ring-4 focus:ring-gray-100";
const baseContentClassName =
"rounded-xl border-2 border-gray-200 bg-white shadow-xl";
const baseItemClassName =
"px-4 py-3 text-sm text-gray-700 transition-colors data-[selected=true]:bg-gray-50 data-[selected=true]:text-gray-900 data-[selected=true]:font-semibold hover:bg-gray-50";
export default function SearchableSelect({
value,
onValueChange,
options,
placeholder,
ariaLabel,
disabled = false,
triggerClassName,
contentClassName,
itemClassName,
searchEnabled,
searchPlaceholder,
emptyMessage,
}: SearchableSelectProps) {
return (
<DropdownSelect
value={value}
onValueChange={onValueChange}
options={options}
placeholder={placeholder}
ariaLabel={ariaLabel}
disabled={disabled}
triggerClassName={cn(baseTriggerClassName, triggerClassName)}
contentClassName={cn(baseContentClassName, contentClassName)}
itemClassName={cn(baseItemClassName, itemClassName)}
searchEnabled={searchEnabled}
searchPlaceholder={searchPlaceholder}
emptyMessage={emptyMessage}
/>
);
}

View File

@@ -0,0 +1,11 @@
export function getApiBaseUrl(): string {
const raw = process.env.NEXT_PUBLIC_API_URL;
if (!raw) {
throw new Error("NEXT_PUBLIC_API_URL is not set.");
}
const normalized = raw.replace(/\/+$/, "");
if (!normalized) {
throw new Error("NEXT_PUBLIC_API_URL is invalid.");
}
return normalized;
}