Add Tailwind + shadcn UI primitives; add React Query + Orval client
This commit is contained in:
3
frontend/.eslintignore
Normal file
3
frontend/.eslintignore
Normal file
@@ -0,0 +1,3 @@
|
||||
tailwind.config.*
|
||||
postcss.config.*
|
||||
orval.config.*
|
||||
@@ -12,6 +12,9 @@ const eslintConfig = defineConfig([
|
||||
"out/**",
|
||||
"build/**",
|
||||
"next-env.d.ts",
|
||||
"tailwind.config.*",
|
||||
"postcss.config.*",
|
||||
"orval.config.*",
|
||||
]),
|
||||
]);
|
||||
|
||||
|
||||
26
frontend/orval.config.ts
Normal file
26
frontend/orval.config.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import { defineConfig } from "orval";
|
||||
|
||||
export default defineConfig({
|
||||
api: {
|
||||
input: {
|
||||
target: "http://127.0.0.1:8000/openapi.json",
|
||||
},
|
||||
output: {
|
||||
mode: "tags-split",
|
||||
target: "src/api/generated/index.ts",
|
||||
schemas: "src/api/generated/model",
|
||||
client: "fetch",
|
||||
prettier: true,
|
||||
override: {
|
||||
mutator: {
|
||||
path: "src/api/mutator.ts",
|
||||
name: "customFetch",
|
||||
},
|
||||
query: {
|
||||
useQuery: true,
|
||||
useMutation: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
2526
frontend/package-lock.json
generated
2526
frontend/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -7,9 +7,11 @@
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"lint": "eslint",
|
||||
"dev:lan": "next dev --hostname 0.0.0.0 --port 3000"
|
||||
"dev:lan": "next dev --hostname 0.0.0.0 --port 3000",
|
||||
"api:gen": "orval --config ./orval.config.ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"@tanstack/react-query": "^5.90.20",
|
||||
"next": "16.1.6",
|
||||
"react": "19.2.3",
|
||||
"react-dom": "19.2.3"
|
||||
@@ -18,8 +20,18 @@
|
||||
"@types/node": "^20",
|
||||
"@types/react": "^19",
|
||||
"@types/react-dom": "^19",
|
||||
"autoprefixer": "^10.4.24",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"eslint": "^9",
|
||||
"eslint-config-next": "16.1.6",
|
||||
"lucide-react": "^0.563.0",
|
||||
"orval": "^8.2.0",
|
||||
"postcss": "^8.5.6",
|
||||
"prettier": "^3.8.1",
|
||||
"tailwind-merge": "^3.4.0",
|
||||
"tailwindcss": "^3.4.17",
|
||||
"tailwindcss-animate": "^1.0.7",
|
||||
"typescript": "^5"
|
||||
}
|
||||
}
|
||||
|
||||
6
frontend/postcss.config.js
Normal file
6
frontend/postcss.config.js
Normal file
@@ -0,0 +1,6 @@
|
||||
module.exports = {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
}
|
||||
69
frontend/src/api/generated/activities/activities.ts
Normal file
69
frontend/src/api/generated/activities/activities.ts
Normal file
@@ -0,0 +1,69 @@
|
||||
/**
|
||||
* Generated by orval v8.2.0 🍺
|
||||
* Do not edit manually.
|
||||
* OpenClaw Agency API
|
||||
* OpenAPI spec version: 0.3.0
|
||||
*/
|
||||
import type {
|
||||
HTTPValidationError,
|
||||
ListActivitiesActivitiesGetParams,
|
||||
} from ".././model";
|
||||
|
||||
import { customFetch } from "../../mutator";
|
||||
|
||||
/**
|
||||
* @summary List Activities
|
||||
*/
|
||||
export type listActivitiesActivitiesGetResponse200 = {
|
||||
data: unknown;
|
||||
status: 200;
|
||||
};
|
||||
|
||||
export type listActivitiesActivitiesGetResponse422 = {
|
||||
data: HTTPValidationError;
|
||||
status: 422;
|
||||
};
|
||||
|
||||
export type listActivitiesActivitiesGetResponseSuccess =
|
||||
listActivitiesActivitiesGetResponse200 & {
|
||||
headers: Headers;
|
||||
};
|
||||
export type listActivitiesActivitiesGetResponseError =
|
||||
listActivitiesActivitiesGetResponse422 & {
|
||||
headers: Headers;
|
||||
};
|
||||
|
||||
export type listActivitiesActivitiesGetResponse =
|
||||
| listActivitiesActivitiesGetResponseSuccess
|
||||
| listActivitiesActivitiesGetResponseError;
|
||||
|
||||
export const getListActivitiesActivitiesGetUrl = (
|
||||
params?: ListActivitiesActivitiesGetParams,
|
||||
) => {
|
||||
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
|
||||
? `/activities?${stringifiedParams}`
|
||||
: `/activities`;
|
||||
};
|
||||
|
||||
export const listActivitiesActivitiesGet = async (
|
||||
params?: ListActivitiesActivitiesGetParams,
|
||||
options?: RequestInit,
|
||||
): Promise<listActivitiesActivitiesGetResponse> => {
|
||||
return customFetch<listActivitiesActivitiesGetResponse>(
|
||||
getListActivitiesActivitiesGetUrl(params),
|
||||
{
|
||||
...options,
|
||||
method: "GET",
|
||||
},
|
||||
);
|
||||
};
|
||||
33
frontend/src/api/generated/default/default.ts
Normal file
33
frontend/src/api/generated/default/default.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* Generated by orval v8.2.0 🍺
|
||||
* Do not edit manually.
|
||||
* OpenClaw Agency API
|
||||
* OpenAPI spec version: 0.3.0
|
||||
*/
|
||||
import { customFetch } from "../../mutator";
|
||||
|
||||
/**
|
||||
* @summary Health
|
||||
*/
|
||||
export type healthHealthGetResponse200 = {
|
||||
data: unknown;
|
||||
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",
|
||||
});
|
||||
};
|
||||
216
frontend/src/api/generated/hr/hr.ts
Normal file
216
frontend/src/api/generated/hr/hr.ts
Normal file
@@ -0,0 +1,216 @@
|
||||
/**
|
||||
* Generated by orval v8.2.0 🍺
|
||||
* Do not edit manually.
|
||||
* OpenClaw Agency API
|
||||
* OpenAPI spec version: 0.3.0
|
||||
*/
|
||||
import type {
|
||||
EmploymentAction,
|
||||
EmploymentActionCreate,
|
||||
HTTPValidationError,
|
||||
HeadcountRequest,
|
||||
HeadcountRequestCreate,
|
||||
HeadcountRequestUpdate,
|
||||
} from ".././model";
|
||||
|
||||
import { customFetch } from "../../mutator";
|
||||
|
||||
/**
|
||||
* @summary List Headcount Requests
|
||||
*/
|
||||
export type listHeadcountRequestsHrHeadcountGetResponse200 = {
|
||||
data: HeadcountRequest[];
|
||||
status: 200;
|
||||
};
|
||||
|
||||
export type listHeadcountRequestsHrHeadcountGetResponseSuccess =
|
||||
listHeadcountRequestsHrHeadcountGetResponse200 & {
|
||||
headers: Headers;
|
||||
};
|
||||
export type listHeadcountRequestsHrHeadcountGetResponse =
|
||||
listHeadcountRequestsHrHeadcountGetResponseSuccess;
|
||||
|
||||
export const getListHeadcountRequestsHrHeadcountGetUrl = () => {
|
||||
return `/hr/headcount`;
|
||||
};
|
||||
|
||||
export const listHeadcountRequestsHrHeadcountGet = async (
|
||||
options?: RequestInit,
|
||||
): Promise<listHeadcountRequestsHrHeadcountGetResponse> => {
|
||||
return customFetch<listHeadcountRequestsHrHeadcountGetResponse>(
|
||||
getListHeadcountRequestsHrHeadcountGetUrl(),
|
||||
{
|
||||
...options,
|
||||
method: "GET",
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* @summary Create Headcount Request
|
||||
*/
|
||||
export type createHeadcountRequestHrHeadcountPostResponse200 = {
|
||||
data: HeadcountRequest;
|
||||
status: 200;
|
||||
};
|
||||
|
||||
export type createHeadcountRequestHrHeadcountPostResponse422 = {
|
||||
data: HTTPValidationError;
|
||||
status: 422;
|
||||
};
|
||||
|
||||
export type createHeadcountRequestHrHeadcountPostResponseSuccess =
|
||||
createHeadcountRequestHrHeadcountPostResponse200 & {
|
||||
headers: Headers;
|
||||
};
|
||||
export type createHeadcountRequestHrHeadcountPostResponseError =
|
||||
createHeadcountRequestHrHeadcountPostResponse422 & {
|
||||
headers: Headers;
|
||||
};
|
||||
|
||||
export type createHeadcountRequestHrHeadcountPostResponse =
|
||||
| createHeadcountRequestHrHeadcountPostResponseSuccess
|
||||
| createHeadcountRequestHrHeadcountPostResponseError;
|
||||
|
||||
export const getCreateHeadcountRequestHrHeadcountPostUrl = () => {
|
||||
return `/hr/headcount`;
|
||||
};
|
||||
|
||||
export const createHeadcountRequestHrHeadcountPost = async (
|
||||
headcountRequestCreate: HeadcountRequestCreate,
|
||||
options?: RequestInit,
|
||||
): Promise<createHeadcountRequestHrHeadcountPostResponse> => {
|
||||
return customFetch<createHeadcountRequestHrHeadcountPostResponse>(
|
||||
getCreateHeadcountRequestHrHeadcountPostUrl(),
|
||||
{
|
||||
...options,
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json", ...options?.headers },
|
||||
body: JSON.stringify(headcountRequestCreate),
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* @summary Update Headcount Request
|
||||
*/
|
||||
export type updateHeadcountRequestHrHeadcountRequestIdPatchResponse200 = {
|
||||
data: HeadcountRequest;
|
||||
status: 200;
|
||||
};
|
||||
|
||||
export type updateHeadcountRequestHrHeadcountRequestIdPatchResponse422 = {
|
||||
data: HTTPValidationError;
|
||||
status: 422;
|
||||
};
|
||||
|
||||
export type updateHeadcountRequestHrHeadcountRequestIdPatchResponseSuccess =
|
||||
updateHeadcountRequestHrHeadcountRequestIdPatchResponse200 & {
|
||||
headers: Headers;
|
||||
};
|
||||
export type updateHeadcountRequestHrHeadcountRequestIdPatchResponseError =
|
||||
updateHeadcountRequestHrHeadcountRequestIdPatchResponse422 & {
|
||||
headers: Headers;
|
||||
};
|
||||
|
||||
export type updateHeadcountRequestHrHeadcountRequestIdPatchResponse =
|
||||
| updateHeadcountRequestHrHeadcountRequestIdPatchResponseSuccess
|
||||
| updateHeadcountRequestHrHeadcountRequestIdPatchResponseError;
|
||||
|
||||
export const getUpdateHeadcountRequestHrHeadcountRequestIdPatchUrl = (
|
||||
requestId: number,
|
||||
) => {
|
||||
return `/hr/headcount/${requestId}`;
|
||||
};
|
||||
|
||||
export const updateHeadcountRequestHrHeadcountRequestIdPatch = async (
|
||||
requestId: number,
|
||||
headcountRequestUpdate: HeadcountRequestUpdate,
|
||||
options?: RequestInit,
|
||||
): Promise<updateHeadcountRequestHrHeadcountRequestIdPatchResponse> => {
|
||||
return customFetch<updateHeadcountRequestHrHeadcountRequestIdPatchResponse>(
|
||||
getUpdateHeadcountRequestHrHeadcountRequestIdPatchUrl(requestId),
|
||||
{
|
||||
...options,
|
||||
method: "PATCH",
|
||||
headers: { "Content-Type": "application/json", ...options?.headers },
|
||||
body: JSON.stringify(headcountRequestUpdate),
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* @summary List Employment Actions
|
||||
*/
|
||||
export type listEmploymentActionsHrActionsGetResponse200 = {
|
||||
data: EmploymentAction[];
|
||||
status: 200;
|
||||
};
|
||||
|
||||
export type listEmploymentActionsHrActionsGetResponseSuccess =
|
||||
listEmploymentActionsHrActionsGetResponse200 & {
|
||||
headers: Headers;
|
||||
};
|
||||
export type listEmploymentActionsHrActionsGetResponse =
|
||||
listEmploymentActionsHrActionsGetResponseSuccess;
|
||||
|
||||
export const getListEmploymentActionsHrActionsGetUrl = () => {
|
||||
return `/hr/actions`;
|
||||
};
|
||||
|
||||
export const listEmploymentActionsHrActionsGet = async (
|
||||
options?: RequestInit,
|
||||
): Promise<listEmploymentActionsHrActionsGetResponse> => {
|
||||
return customFetch<listEmploymentActionsHrActionsGetResponse>(
|
||||
getListEmploymentActionsHrActionsGetUrl(),
|
||||
{
|
||||
...options,
|
||||
method: "GET",
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* @summary Create Employment Action
|
||||
*/
|
||||
export type createEmploymentActionHrActionsPostResponse200 = {
|
||||
data: EmploymentAction;
|
||||
status: 200;
|
||||
};
|
||||
|
||||
export type createEmploymentActionHrActionsPostResponse422 = {
|
||||
data: HTTPValidationError;
|
||||
status: 422;
|
||||
};
|
||||
|
||||
export type createEmploymentActionHrActionsPostResponseSuccess =
|
||||
createEmploymentActionHrActionsPostResponse200 & {
|
||||
headers: Headers;
|
||||
};
|
||||
export type createEmploymentActionHrActionsPostResponseError =
|
||||
createEmploymentActionHrActionsPostResponse422 & {
|
||||
headers: Headers;
|
||||
};
|
||||
|
||||
export type createEmploymentActionHrActionsPostResponse =
|
||||
| createEmploymentActionHrActionsPostResponseSuccess
|
||||
| createEmploymentActionHrActionsPostResponseError;
|
||||
|
||||
export const getCreateEmploymentActionHrActionsPostUrl = () => {
|
||||
return `/hr/actions`;
|
||||
};
|
||||
|
||||
export const createEmploymentActionHrActionsPost = async (
|
||||
employmentActionCreate: EmploymentActionCreate,
|
||||
options?: RequestInit,
|
||||
): Promise<createEmploymentActionHrActionsPostResponse> => {
|
||||
return customFetch<createEmploymentActionHrActionsPostResponse>(
|
||||
getCreateEmploymentActionHrActionsPostUrl(),
|
||||
{
|
||||
...options,
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json", ...options?.headers },
|
||||
body: JSON.stringify(employmentActionCreate),
|
||||
},
|
||||
);
|
||||
};
|
||||
12
frontend/src/api/generated/model/department.ts
Normal file
12
frontend/src/api/generated/model/department.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
/**
|
||||
* Generated by orval v8.2.0 🍺
|
||||
* Do not edit manually.
|
||||
* OpenClaw Agency API
|
||||
* OpenAPI spec version: 0.3.0
|
||||
*/
|
||||
|
||||
export interface Department {
|
||||
id?: number | null;
|
||||
name: string;
|
||||
head_employee_id?: number | null;
|
||||
}
|
||||
11
frontend/src/api/generated/model/departmentCreate.ts
Normal file
11
frontend/src/api/generated/model/departmentCreate.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
/**
|
||||
* Generated by orval v8.2.0 🍺
|
||||
* Do not edit manually.
|
||||
* OpenClaw Agency API
|
||||
* OpenAPI spec version: 0.3.0
|
||||
*/
|
||||
|
||||
export interface DepartmentCreate {
|
||||
name: string;
|
||||
head_employee_id?: number | null;
|
||||
}
|
||||
11
frontend/src/api/generated/model/departmentUpdate.ts
Normal file
11
frontend/src/api/generated/model/departmentUpdate.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
/**
|
||||
* Generated by orval v8.2.0 🍺
|
||||
* Do not edit manually.
|
||||
* OpenClaw Agency API
|
||||
* OpenAPI spec version: 0.3.0
|
||||
*/
|
||||
|
||||
export interface DepartmentUpdate {
|
||||
name?: string | null;
|
||||
head_employee_id?: number | null;
|
||||
}
|
||||
16
frontend/src/api/generated/model/employee.ts
Normal file
16
frontend/src/api/generated/model/employee.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
/**
|
||||
* Generated by orval v8.2.0 🍺
|
||||
* Do not edit manually.
|
||||
* OpenClaw Agency API
|
||||
* OpenAPI spec version: 0.3.0
|
||||
*/
|
||||
|
||||
export interface Employee {
|
||||
id?: number | null;
|
||||
name: string;
|
||||
employee_type: string;
|
||||
department_id?: number | null;
|
||||
manager_id?: number | null;
|
||||
title?: string | null;
|
||||
status?: string;
|
||||
}
|
||||
15
frontend/src/api/generated/model/employeeCreate.ts
Normal file
15
frontend/src/api/generated/model/employeeCreate.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
/**
|
||||
* Generated by orval v8.2.0 🍺
|
||||
* Do not edit manually.
|
||||
* OpenClaw Agency API
|
||||
* OpenAPI spec version: 0.3.0
|
||||
*/
|
||||
|
||||
export interface EmployeeCreate {
|
||||
name: string;
|
||||
employee_type: string;
|
||||
department_id?: number | null;
|
||||
manager_id?: number | null;
|
||||
title?: string | null;
|
||||
status?: string;
|
||||
}
|
||||
15
frontend/src/api/generated/model/employeeUpdate.ts
Normal file
15
frontend/src/api/generated/model/employeeUpdate.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
/**
|
||||
* Generated by orval v8.2.0 🍺
|
||||
* Do not edit manually.
|
||||
* OpenClaw Agency API
|
||||
* OpenAPI spec version: 0.3.0
|
||||
*/
|
||||
|
||||
export interface EmployeeUpdate {
|
||||
name?: string | null;
|
||||
employee_type?: string | null;
|
||||
department_id?: number | null;
|
||||
manager_id?: number | null;
|
||||
title?: string | null;
|
||||
status?: string | null;
|
||||
}
|
||||
15
frontend/src/api/generated/model/employmentAction.ts
Normal file
15
frontend/src/api/generated/model/employmentAction.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
/**
|
||||
* Generated by orval v8.2.0 🍺
|
||||
* Do not edit manually.
|
||||
* OpenClaw Agency API
|
||||
* OpenAPI spec version: 0.3.0
|
||||
*/
|
||||
|
||||
export interface EmploymentAction {
|
||||
id?: number | null;
|
||||
employee_id: number;
|
||||
issued_by_employee_id: number;
|
||||
action_type: string;
|
||||
notes?: string | null;
|
||||
created_at?: string;
|
||||
}
|
||||
13
frontend/src/api/generated/model/employmentActionCreate.ts
Normal file
13
frontend/src/api/generated/model/employmentActionCreate.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
* Generated by orval v8.2.0 🍺
|
||||
* Do not edit manually.
|
||||
* OpenClaw Agency API
|
||||
* OpenAPI spec version: 0.3.0
|
||||
*/
|
||||
|
||||
export interface EmploymentActionCreate {
|
||||
employee_id: number;
|
||||
issued_by_employee_id: number;
|
||||
action_type: string;
|
||||
notes?: string | null;
|
||||
}
|
||||
11
frontend/src/api/generated/model/hTTPValidationError.ts
Normal file
11
frontend/src/api/generated/model/hTTPValidationError.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
/**
|
||||
* Generated by orval v8.2.0 🍺
|
||||
* Do not edit manually.
|
||||
* OpenClaw Agency API
|
||||
* OpenAPI spec version: 0.3.0
|
||||
*/
|
||||
import type { ValidationError } from "./validationError";
|
||||
|
||||
export interface HTTPValidationError {
|
||||
detail?: ValidationError[];
|
||||
}
|
||||
18
frontend/src/api/generated/model/headcountRequest.ts
Normal file
18
frontend/src/api/generated/model/headcountRequest.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
/**
|
||||
* Generated by orval v8.2.0 🍺
|
||||
* Do not edit manually.
|
||||
* OpenClaw Agency API
|
||||
* OpenAPI spec version: 0.3.0
|
||||
*/
|
||||
|
||||
export interface HeadcountRequest {
|
||||
id?: number | null;
|
||||
department_id: number;
|
||||
requested_by_manager_id: number;
|
||||
role_title: string;
|
||||
employee_type: string;
|
||||
quantity?: number;
|
||||
justification?: string | null;
|
||||
status?: string;
|
||||
created_at?: string;
|
||||
}
|
||||
15
frontend/src/api/generated/model/headcountRequestCreate.ts
Normal file
15
frontend/src/api/generated/model/headcountRequestCreate.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
/**
|
||||
* Generated by orval v8.2.0 🍺
|
||||
* Do not edit manually.
|
||||
* OpenClaw Agency API
|
||||
* OpenAPI spec version: 0.3.0
|
||||
*/
|
||||
|
||||
export interface HeadcountRequestCreate {
|
||||
department_id: number;
|
||||
requested_by_manager_id: number;
|
||||
role_title: string;
|
||||
employee_type: string;
|
||||
quantity?: number;
|
||||
justification?: string | null;
|
||||
}
|
||||
11
frontend/src/api/generated/model/headcountRequestUpdate.ts
Normal file
11
frontend/src/api/generated/model/headcountRequestUpdate.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
/**
|
||||
* Generated by orval v8.2.0 🍺
|
||||
* Do not edit manually.
|
||||
* OpenClaw Agency API
|
||||
* OpenAPI spec version: 0.3.0
|
||||
*/
|
||||
|
||||
export interface HeadcountRequestUpdate {
|
||||
status?: string | null;
|
||||
justification?: string | null;
|
||||
}
|
||||
31
frontend/src/api/generated/model/index.ts
Normal file
31
frontend/src/api/generated/model/index.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* Generated by orval v8.2.0 🍺
|
||||
* Do not edit manually.
|
||||
* OpenClaw Agency API
|
||||
* OpenAPI spec version: 0.3.0
|
||||
*/
|
||||
|
||||
export * from "./department";
|
||||
export * from "./departmentCreate";
|
||||
export * from "./departmentUpdate";
|
||||
export * from "./employee";
|
||||
export * from "./employeeCreate";
|
||||
export * from "./employeeUpdate";
|
||||
export * from "./employmentAction";
|
||||
export * from "./employmentActionCreate";
|
||||
export * from "./headcountRequest";
|
||||
export * from "./headcountRequestCreate";
|
||||
export * from "./headcountRequestUpdate";
|
||||
export * from "./hTTPValidationError";
|
||||
export * from "./listActivitiesActivitiesGetParams";
|
||||
export * from "./listTaskCommentsTaskCommentsGetParams";
|
||||
export * from "./listTasksTasksGetParams";
|
||||
export * from "./project";
|
||||
export * from "./projectCreate";
|
||||
export * from "./projectUpdate";
|
||||
export * from "./task";
|
||||
export * from "./taskComment";
|
||||
export * from "./taskCommentCreate";
|
||||
export * from "./taskCreate";
|
||||
export * from "./taskUpdate";
|
||||
export * from "./validationError";
|
||||
@@ -0,0 +1,10 @@
|
||||
/**
|
||||
* Generated by orval v8.2.0 🍺
|
||||
* Do not edit manually.
|
||||
* OpenClaw Agency API
|
||||
* OpenAPI spec version: 0.3.0
|
||||
*/
|
||||
|
||||
export type ListActivitiesActivitiesGetParams = {
|
||||
limit?: number;
|
||||
};
|
||||
@@ -0,0 +1,10 @@
|
||||
/**
|
||||
* Generated by orval v8.2.0 🍺
|
||||
* Do not edit manually.
|
||||
* OpenClaw Agency API
|
||||
* OpenAPI spec version: 0.3.0
|
||||
*/
|
||||
|
||||
export type ListTaskCommentsTaskCommentsGetParams = {
|
||||
task_id: number;
|
||||
};
|
||||
10
frontend/src/api/generated/model/listTasksTasksGetParams.ts
Normal file
10
frontend/src/api/generated/model/listTasksTasksGetParams.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
/**
|
||||
* Generated by orval v8.2.0 🍺
|
||||
* Do not edit manually.
|
||||
* OpenClaw Agency API
|
||||
* OpenAPI spec version: 0.3.0
|
||||
*/
|
||||
|
||||
export type ListTasksTasksGetParams = {
|
||||
project_id?: number | null;
|
||||
};
|
||||
12
frontend/src/api/generated/model/project.ts
Normal file
12
frontend/src/api/generated/model/project.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
/**
|
||||
* Generated by orval v8.2.0 🍺
|
||||
* Do not edit manually.
|
||||
* OpenClaw Agency API
|
||||
* OpenAPI spec version: 0.3.0
|
||||
*/
|
||||
|
||||
export interface Project {
|
||||
id?: number | null;
|
||||
name: string;
|
||||
status?: string;
|
||||
}
|
||||
11
frontend/src/api/generated/model/projectCreate.ts
Normal file
11
frontend/src/api/generated/model/projectCreate.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
/**
|
||||
* Generated by orval v8.2.0 🍺
|
||||
* Do not edit manually.
|
||||
* OpenClaw Agency API
|
||||
* OpenAPI spec version: 0.3.0
|
||||
*/
|
||||
|
||||
export interface ProjectCreate {
|
||||
name: string;
|
||||
status?: string;
|
||||
}
|
||||
11
frontend/src/api/generated/model/projectUpdate.ts
Normal file
11
frontend/src/api/generated/model/projectUpdate.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
/**
|
||||
* Generated by orval v8.2.0 🍺
|
||||
* Do not edit manually.
|
||||
* OpenClaw Agency API
|
||||
* OpenAPI spec version: 0.3.0
|
||||
*/
|
||||
|
||||
export interface ProjectUpdate {
|
||||
name?: string | null;
|
||||
status?: string | null;
|
||||
}
|
||||
19
frontend/src/api/generated/model/task.ts
Normal file
19
frontend/src/api/generated/model/task.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
/**
|
||||
* Generated by orval v8.2.0 🍺
|
||||
* Do not edit manually.
|
||||
* OpenClaw Agency API
|
||||
* OpenAPI spec version: 0.3.0
|
||||
*/
|
||||
|
||||
export interface Task {
|
||||
id?: number | null;
|
||||
project_id: number;
|
||||
title: string;
|
||||
description?: string | null;
|
||||
status?: string;
|
||||
assignee_employee_id?: number | null;
|
||||
reviewer_employee_id?: number | null;
|
||||
created_by_employee_id?: number | null;
|
||||
created_at?: string;
|
||||
updated_at?: string;
|
||||
}
|
||||
14
frontend/src/api/generated/model/taskComment.ts
Normal file
14
frontend/src/api/generated/model/taskComment.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* Generated by orval v8.2.0 🍺
|
||||
* Do not edit manually.
|
||||
* OpenClaw Agency API
|
||||
* OpenAPI spec version: 0.3.0
|
||||
*/
|
||||
|
||||
export interface TaskComment {
|
||||
id?: number | null;
|
||||
task_id: number;
|
||||
author_employee_id?: number | null;
|
||||
body: string;
|
||||
created_at?: string;
|
||||
}
|
||||
12
frontend/src/api/generated/model/taskCommentCreate.ts
Normal file
12
frontend/src/api/generated/model/taskCommentCreate.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
/**
|
||||
* Generated by orval v8.2.0 🍺
|
||||
* Do not edit manually.
|
||||
* OpenClaw Agency API
|
||||
* OpenAPI spec version: 0.3.0
|
||||
*/
|
||||
|
||||
export interface TaskCommentCreate {
|
||||
task_id: number;
|
||||
author_employee_id?: number | null;
|
||||
body: string;
|
||||
}
|
||||
16
frontend/src/api/generated/model/taskCreate.ts
Normal file
16
frontend/src/api/generated/model/taskCreate.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
/**
|
||||
* Generated by orval v8.2.0 🍺
|
||||
* Do not edit manually.
|
||||
* OpenClaw Agency API
|
||||
* OpenAPI spec version: 0.3.0
|
||||
*/
|
||||
|
||||
export interface TaskCreate {
|
||||
project_id: number;
|
||||
title: string;
|
||||
description?: string | null;
|
||||
status?: string;
|
||||
assignee_employee_id?: number | null;
|
||||
reviewer_employee_id?: number | null;
|
||||
created_by_employee_id?: number | null;
|
||||
}
|
||||
14
frontend/src/api/generated/model/taskUpdate.ts
Normal file
14
frontend/src/api/generated/model/taskUpdate.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* Generated by orval v8.2.0 🍺
|
||||
* Do not edit manually.
|
||||
* OpenClaw Agency API
|
||||
* OpenAPI spec version: 0.3.0
|
||||
*/
|
||||
|
||||
export interface TaskUpdate {
|
||||
title?: string | null;
|
||||
description?: string | null;
|
||||
status?: string | null;
|
||||
assignee_employee_id?: number | null;
|
||||
reviewer_employee_id?: number | null;
|
||||
}
|
||||
12
frontend/src/api/generated/model/validationError.ts
Normal file
12
frontend/src/api/generated/model/validationError.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
/**
|
||||
* Generated by orval v8.2.0 🍺
|
||||
* Do not edit manually.
|
||||
* OpenClaw Agency API
|
||||
* OpenAPI spec version: 0.3.0
|
||||
*/
|
||||
|
||||
export interface ValidationError {
|
||||
loc: (string | number)[];
|
||||
msg: string;
|
||||
type: string;
|
||||
}
|
||||
265
frontend/src/api/generated/org/org.ts
Normal file
265
frontend/src/api/generated/org/org.ts
Normal file
@@ -0,0 +1,265 @@
|
||||
/**
|
||||
* Generated by orval v8.2.0 🍺
|
||||
* Do not edit manually.
|
||||
* OpenClaw Agency API
|
||||
* OpenAPI spec version: 0.3.0
|
||||
*/
|
||||
import type {
|
||||
Department,
|
||||
DepartmentCreate,
|
||||
DepartmentUpdate,
|
||||
Employee,
|
||||
EmployeeCreate,
|
||||
EmployeeUpdate,
|
||||
HTTPValidationError,
|
||||
} from ".././model";
|
||||
|
||||
import { customFetch } from "../../mutator";
|
||||
|
||||
/**
|
||||
* @summary List Departments
|
||||
*/
|
||||
export type listDepartmentsDepartmentsGetResponse200 = {
|
||||
data: Department[];
|
||||
status: 200;
|
||||
};
|
||||
|
||||
export type listDepartmentsDepartmentsGetResponseSuccess =
|
||||
listDepartmentsDepartmentsGetResponse200 & {
|
||||
headers: Headers;
|
||||
};
|
||||
export type listDepartmentsDepartmentsGetResponse =
|
||||
listDepartmentsDepartmentsGetResponseSuccess;
|
||||
|
||||
export const getListDepartmentsDepartmentsGetUrl = () => {
|
||||
return `/departments`;
|
||||
};
|
||||
|
||||
export const listDepartmentsDepartmentsGet = async (
|
||||
options?: RequestInit,
|
||||
): Promise<listDepartmentsDepartmentsGetResponse> => {
|
||||
return customFetch<listDepartmentsDepartmentsGetResponse>(
|
||||
getListDepartmentsDepartmentsGetUrl(),
|
||||
{
|
||||
...options,
|
||||
method: "GET",
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* @summary Create Department
|
||||
*/
|
||||
export type createDepartmentDepartmentsPostResponse200 = {
|
||||
data: Department;
|
||||
status: 200;
|
||||
};
|
||||
|
||||
export type createDepartmentDepartmentsPostResponse422 = {
|
||||
data: HTTPValidationError;
|
||||
status: 422;
|
||||
};
|
||||
|
||||
export type createDepartmentDepartmentsPostResponseSuccess =
|
||||
createDepartmentDepartmentsPostResponse200 & {
|
||||
headers: Headers;
|
||||
};
|
||||
export type createDepartmentDepartmentsPostResponseError =
|
||||
createDepartmentDepartmentsPostResponse422 & {
|
||||
headers: Headers;
|
||||
};
|
||||
|
||||
export type createDepartmentDepartmentsPostResponse =
|
||||
| createDepartmentDepartmentsPostResponseSuccess
|
||||
| createDepartmentDepartmentsPostResponseError;
|
||||
|
||||
export const getCreateDepartmentDepartmentsPostUrl = () => {
|
||||
return `/departments`;
|
||||
};
|
||||
|
||||
export const createDepartmentDepartmentsPost = async (
|
||||
departmentCreate: DepartmentCreate,
|
||||
options?: RequestInit,
|
||||
): Promise<createDepartmentDepartmentsPostResponse> => {
|
||||
return customFetch<createDepartmentDepartmentsPostResponse>(
|
||||
getCreateDepartmentDepartmentsPostUrl(),
|
||||
{
|
||||
...options,
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json", ...options?.headers },
|
||||
body: JSON.stringify(departmentCreate),
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* @summary Update Department
|
||||
*/
|
||||
export type updateDepartmentDepartmentsDepartmentIdPatchResponse200 = {
|
||||
data: Department;
|
||||
status: 200;
|
||||
};
|
||||
|
||||
export type updateDepartmentDepartmentsDepartmentIdPatchResponse422 = {
|
||||
data: HTTPValidationError;
|
||||
status: 422;
|
||||
};
|
||||
|
||||
export type updateDepartmentDepartmentsDepartmentIdPatchResponseSuccess =
|
||||
updateDepartmentDepartmentsDepartmentIdPatchResponse200 & {
|
||||
headers: Headers;
|
||||
};
|
||||
export type updateDepartmentDepartmentsDepartmentIdPatchResponseError =
|
||||
updateDepartmentDepartmentsDepartmentIdPatchResponse422 & {
|
||||
headers: Headers;
|
||||
};
|
||||
|
||||
export type updateDepartmentDepartmentsDepartmentIdPatchResponse =
|
||||
| updateDepartmentDepartmentsDepartmentIdPatchResponseSuccess
|
||||
| updateDepartmentDepartmentsDepartmentIdPatchResponseError;
|
||||
|
||||
export const getUpdateDepartmentDepartmentsDepartmentIdPatchUrl = (
|
||||
departmentId: number,
|
||||
) => {
|
||||
return `/departments/${departmentId}`;
|
||||
};
|
||||
|
||||
export const updateDepartmentDepartmentsDepartmentIdPatch = async (
|
||||
departmentId: number,
|
||||
departmentUpdate: DepartmentUpdate,
|
||||
options?: RequestInit,
|
||||
): Promise<updateDepartmentDepartmentsDepartmentIdPatchResponse> => {
|
||||
return customFetch<updateDepartmentDepartmentsDepartmentIdPatchResponse>(
|
||||
getUpdateDepartmentDepartmentsDepartmentIdPatchUrl(departmentId),
|
||||
{
|
||||
...options,
|
||||
method: "PATCH",
|
||||
headers: { "Content-Type": "application/json", ...options?.headers },
|
||||
body: JSON.stringify(departmentUpdate),
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* @summary List Employees
|
||||
*/
|
||||
export type listEmployeesEmployeesGetResponse200 = {
|
||||
data: Employee[];
|
||||
status: 200;
|
||||
};
|
||||
|
||||
export type listEmployeesEmployeesGetResponseSuccess =
|
||||
listEmployeesEmployeesGetResponse200 & {
|
||||
headers: Headers;
|
||||
};
|
||||
export type listEmployeesEmployeesGetResponse =
|
||||
listEmployeesEmployeesGetResponseSuccess;
|
||||
|
||||
export const getListEmployeesEmployeesGetUrl = () => {
|
||||
return `/employees`;
|
||||
};
|
||||
|
||||
export const listEmployeesEmployeesGet = async (
|
||||
options?: RequestInit,
|
||||
): Promise<listEmployeesEmployeesGetResponse> => {
|
||||
return customFetch<listEmployeesEmployeesGetResponse>(
|
||||
getListEmployeesEmployeesGetUrl(),
|
||||
{
|
||||
...options,
|
||||
method: "GET",
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* @summary Create Employee
|
||||
*/
|
||||
export type createEmployeeEmployeesPostResponse200 = {
|
||||
data: Employee;
|
||||
status: 200;
|
||||
};
|
||||
|
||||
export type createEmployeeEmployeesPostResponse422 = {
|
||||
data: HTTPValidationError;
|
||||
status: 422;
|
||||
};
|
||||
|
||||
export type createEmployeeEmployeesPostResponseSuccess =
|
||||
createEmployeeEmployeesPostResponse200 & {
|
||||
headers: Headers;
|
||||
};
|
||||
export type createEmployeeEmployeesPostResponseError =
|
||||
createEmployeeEmployeesPostResponse422 & {
|
||||
headers: Headers;
|
||||
};
|
||||
|
||||
export type createEmployeeEmployeesPostResponse =
|
||||
| createEmployeeEmployeesPostResponseSuccess
|
||||
| createEmployeeEmployeesPostResponseError;
|
||||
|
||||
export const getCreateEmployeeEmployeesPostUrl = () => {
|
||||
return `/employees`;
|
||||
};
|
||||
|
||||
export const createEmployeeEmployeesPost = async (
|
||||
employeeCreate: EmployeeCreate,
|
||||
options?: RequestInit,
|
||||
): Promise<createEmployeeEmployeesPostResponse> => {
|
||||
return customFetch<createEmployeeEmployeesPostResponse>(
|
||||
getCreateEmployeeEmployeesPostUrl(),
|
||||
{
|
||||
...options,
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json", ...options?.headers },
|
||||
body: JSON.stringify(employeeCreate),
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* @summary Update Employee
|
||||
*/
|
||||
export type updateEmployeeEmployeesEmployeeIdPatchResponse200 = {
|
||||
data: Employee;
|
||||
status: 200;
|
||||
};
|
||||
|
||||
export type updateEmployeeEmployeesEmployeeIdPatchResponse422 = {
|
||||
data: HTTPValidationError;
|
||||
status: 422;
|
||||
};
|
||||
|
||||
export type updateEmployeeEmployeesEmployeeIdPatchResponseSuccess =
|
||||
updateEmployeeEmployeesEmployeeIdPatchResponse200 & {
|
||||
headers: Headers;
|
||||
};
|
||||
export type updateEmployeeEmployeesEmployeeIdPatchResponseError =
|
||||
updateEmployeeEmployeesEmployeeIdPatchResponse422 & {
|
||||
headers: Headers;
|
||||
};
|
||||
|
||||
export type updateEmployeeEmployeesEmployeeIdPatchResponse =
|
||||
| updateEmployeeEmployeesEmployeeIdPatchResponseSuccess
|
||||
| updateEmployeeEmployeesEmployeeIdPatchResponseError;
|
||||
|
||||
export const getUpdateEmployeeEmployeesEmployeeIdPatchUrl = (
|
||||
employeeId: number,
|
||||
) => {
|
||||
return `/employees/${employeeId}`;
|
||||
};
|
||||
|
||||
export const updateEmployeeEmployeesEmployeeIdPatch = async (
|
||||
employeeId: number,
|
||||
employeeUpdate: EmployeeUpdate,
|
||||
options?: RequestInit,
|
||||
): Promise<updateEmployeeEmployeesEmployeeIdPatchResponse> => {
|
||||
return customFetch<updateEmployeeEmployeesEmployeeIdPatchResponse>(
|
||||
getUpdateEmployeeEmployeesEmployeeIdPatchUrl(employeeId),
|
||||
{
|
||||
...options,
|
||||
method: "PATCH",
|
||||
headers: { "Content-Type": "application/json", ...options?.headers },
|
||||
body: JSON.stringify(employeeUpdate),
|
||||
},
|
||||
);
|
||||
};
|
||||
138
frontend/src/api/generated/projects/projects.ts
Normal file
138
frontend/src/api/generated/projects/projects.ts
Normal file
@@ -0,0 +1,138 @@
|
||||
/**
|
||||
* Generated by orval v8.2.0 🍺
|
||||
* Do not edit manually.
|
||||
* OpenClaw Agency API
|
||||
* OpenAPI spec version: 0.3.0
|
||||
*/
|
||||
import type {
|
||||
HTTPValidationError,
|
||||
Project,
|
||||
ProjectCreate,
|
||||
ProjectUpdate,
|
||||
} from ".././model";
|
||||
|
||||
import { customFetch } from "../../mutator";
|
||||
|
||||
/**
|
||||
* @summary List Projects
|
||||
*/
|
||||
export type listProjectsProjectsGetResponse200 = {
|
||||
data: Project[];
|
||||
status: 200;
|
||||
};
|
||||
|
||||
export type listProjectsProjectsGetResponseSuccess =
|
||||
listProjectsProjectsGetResponse200 & {
|
||||
headers: Headers;
|
||||
};
|
||||
export type listProjectsProjectsGetResponse =
|
||||
listProjectsProjectsGetResponseSuccess;
|
||||
|
||||
export const getListProjectsProjectsGetUrl = () => {
|
||||
return `/projects`;
|
||||
};
|
||||
|
||||
export const listProjectsProjectsGet = async (
|
||||
options?: RequestInit,
|
||||
): Promise<listProjectsProjectsGetResponse> => {
|
||||
return customFetch<listProjectsProjectsGetResponse>(
|
||||
getListProjectsProjectsGetUrl(),
|
||||
{
|
||||
...options,
|
||||
method: "GET",
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* @summary Create Project
|
||||
*/
|
||||
export type createProjectProjectsPostResponse200 = {
|
||||
data: Project;
|
||||
status: 200;
|
||||
};
|
||||
|
||||
export type createProjectProjectsPostResponse422 = {
|
||||
data: HTTPValidationError;
|
||||
status: 422;
|
||||
};
|
||||
|
||||
export type createProjectProjectsPostResponseSuccess =
|
||||
createProjectProjectsPostResponse200 & {
|
||||
headers: Headers;
|
||||
};
|
||||
export type createProjectProjectsPostResponseError =
|
||||
createProjectProjectsPostResponse422 & {
|
||||
headers: Headers;
|
||||
};
|
||||
|
||||
export type createProjectProjectsPostResponse =
|
||||
| createProjectProjectsPostResponseSuccess
|
||||
| createProjectProjectsPostResponseError;
|
||||
|
||||
export const getCreateProjectProjectsPostUrl = () => {
|
||||
return `/projects`;
|
||||
};
|
||||
|
||||
export const createProjectProjectsPost = async (
|
||||
projectCreate: ProjectCreate,
|
||||
options?: RequestInit,
|
||||
): Promise<createProjectProjectsPostResponse> => {
|
||||
return customFetch<createProjectProjectsPostResponse>(
|
||||
getCreateProjectProjectsPostUrl(),
|
||||
{
|
||||
...options,
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json", ...options?.headers },
|
||||
body: JSON.stringify(projectCreate),
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* @summary Update Project
|
||||
*/
|
||||
export type updateProjectProjectsProjectIdPatchResponse200 = {
|
||||
data: Project;
|
||||
status: 200;
|
||||
};
|
||||
|
||||
export type updateProjectProjectsProjectIdPatchResponse422 = {
|
||||
data: HTTPValidationError;
|
||||
status: 422;
|
||||
};
|
||||
|
||||
export type updateProjectProjectsProjectIdPatchResponseSuccess =
|
||||
updateProjectProjectsProjectIdPatchResponse200 & {
|
||||
headers: Headers;
|
||||
};
|
||||
export type updateProjectProjectsProjectIdPatchResponseError =
|
||||
updateProjectProjectsProjectIdPatchResponse422 & {
|
||||
headers: Headers;
|
||||
};
|
||||
|
||||
export type updateProjectProjectsProjectIdPatchResponse =
|
||||
| updateProjectProjectsProjectIdPatchResponseSuccess
|
||||
| updateProjectProjectsProjectIdPatchResponseError;
|
||||
|
||||
export const getUpdateProjectProjectsProjectIdPatchUrl = (
|
||||
projectId: number,
|
||||
) => {
|
||||
return `/projects/${projectId}`;
|
||||
};
|
||||
|
||||
export const updateProjectProjectsProjectIdPatch = async (
|
||||
projectId: number,
|
||||
projectUpdate: ProjectUpdate,
|
||||
options?: RequestInit,
|
||||
): Promise<updateProjectProjectsProjectIdPatchResponse> => {
|
||||
return customFetch<updateProjectProjectsProjectIdPatchResponse>(
|
||||
getUpdateProjectProjectsProjectIdPatchUrl(projectId),
|
||||
{
|
||||
...options,
|
||||
method: "PATCH",
|
||||
headers: { "Content-Type": "application/json", ...options?.headers },
|
||||
body: JSON.stringify(projectUpdate),
|
||||
},
|
||||
);
|
||||
};
|
||||
304
frontend/src/api/generated/work/work.ts
Normal file
304
frontend/src/api/generated/work/work.ts
Normal file
@@ -0,0 +1,304 @@
|
||||
/**
|
||||
* Generated by orval v8.2.0 🍺
|
||||
* Do not edit manually.
|
||||
* OpenClaw Agency API
|
||||
* OpenAPI spec version: 0.3.0
|
||||
*/
|
||||
import type {
|
||||
HTTPValidationError,
|
||||
ListTaskCommentsTaskCommentsGetParams,
|
||||
ListTasksTasksGetParams,
|
||||
Task,
|
||||
TaskComment,
|
||||
TaskCommentCreate,
|
||||
TaskCreate,
|
||||
TaskUpdate,
|
||||
} from ".././model";
|
||||
|
||||
import { customFetch } from "../../mutator";
|
||||
|
||||
/**
|
||||
* @summary List Tasks
|
||||
*/
|
||||
export type listTasksTasksGetResponse200 = {
|
||||
data: Task[];
|
||||
status: 200;
|
||||
};
|
||||
|
||||
export type listTasksTasksGetResponse422 = {
|
||||
data: HTTPValidationError;
|
||||
status: 422;
|
||||
};
|
||||
|
||||
export type listTasksTasksGetResponseSuccess = listTasksTasksGetResponse200 & {
|
||||
headers: Headers;
|
||||
};
|
||||
export type listTasksTasksGetResponseError = listTasksTasksGetResponse422 & {
|
||||
headers: Headers;
|
||||
};
|
||||
|
||||
export type listTasksTasksGetResponse =
|
||||
| listTasksTasksGetResponseSuccess
|
||||
| listTasksTasksGetResponseError;
|
||||
|
||||
export const getListTasksTasksGetUrl = (params?: ListTasksTasksGetParams) => {
|
||||
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
|
||||
? `/tasks?${stringifiedParams}`
|
||||
: `/tasks`;
|
||||
};
|
||||
|
||||
export const listTasksTasksGet = async (
|
||||
params?: ListTasksTasksGetParams,
|
||||
options?: RequestInit,
|
||||
): Promise<listTasksTasksGetResponse> => {
|
||||
return customFetch<listTasksTasksGetResponse>(
|
||||
getListTasksTasksGetUrl(params),
|
||||
{
|
||||
...options,
|
||||
method: "GET",
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* @summary Create Task
|
||||
*/
|
||||
export type createTaskTasksPostResponse200 = {
|
||||
data: Task;
|
||||
status: 200;
|
||||
};
|
||||
|
||||
export type createTaskTasksPostResponse422 = {
|
||||
data: HTTPValidationError;
|
||||
status: 422;
|
||||
};
|
||||
|
||||
export type createTaskTasksPostResponseSuccess =
|
||||
createTaskTasksPostResponse200 & {
|
||||
headers: Headers;
|
||||
};
|
||||
export type createTaskTasksPostResponseError =
|
||||
createTaskTasksPostResponse422 & {
|
||||
headers: Headers;
|
||||
};
|
||||
|
||||
export type createTaskTasksPostResponse =
|
||||
| createTaskTasksPostResponseSuccess
|
||||
| createTaskTasksPostResponseError;
|
||||
|
||||
export const getCreateTaskTasksPostUrl = () => {
|
||||
return `/tasks`;
|
||||
};
|
||||
|
||||
export const createTaskTasksPost = async (
|
||||
taskCreate: TaskCreate,
|
||||
options?: RequestInit,
|
||||
): Promise<createTaskTasksPostResponse> => {
|
||||
return customFetch<createTaskTasksPostResponse>(getCreateTaskTasksPostUrl(), {
|
||||
...options,
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json", ...options?.headers },
|
||||
body: JSON.stringify(taskCreate),
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @summary Update Task
|
||||
*/
|
||||
export type updateTaskTasksTaskIdPatchResponse200 = {
|
||||
data: Task;
|
||||
status: 200;
|
||||
};
|
||||
|
||||
export type updateTaskTasksTaskIdPatchResponse422 = {
|
||||
data: HTTPValidationError;
|
||||
status: 422;
|
||||
};
|
||||
|
||||
export type updateTaskTasksTaskIdPatchResponseSuccess =
|
||||
updateTaskTasksTaskIdPatchResponse200 & {
|
||||
headers: Headers;
|
||||
};
|
||||
export type updateTaskTasksTaskIdPatchResponseError =
|
||||
updateTaskTasksTaskIdPatchResponse422 & {
|
||||
headers: Headers;
|
||||
};
|
||||
|
||||
export type updateTaskTasksTaskIdPatchResponse =
|
||||
| updateTaskTasksTaskIdPatchResponseSuccess
|
||||
| updateTaskTasksTaskIdPatchResponseError;
|
||||
|
||||
export const getUpdateTaskTasksTaskIdPatchUrl = (taskId: number) => {
|
||||
return `/tasks/${taskId}`;
|
||||
};
|
||||
|
||||
export const updateTaskTasksTaskIdPatch = async (
|
||||
taskId: number,
|
||||
taskUpdate: TaskUpdate,
|
||||
options?: RequestInit,
|
||||
): Promise<updateTaskTasksTaskIdPatchResponse> => {
|
||||
return customFetch<updateTaskTasksTaskIdPatchResponse>(
|
||||
getUpdateTaskTasksTaskIdPatchUrl(taskId),
|
||||
{
|
||||
...options,
|
||||
method: "PATCH",
|
||||
headers: { "Content-Type": "application/json", ...options?.headers },
|
||||
body: JSON.stringify(taskUpdate),
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* @summary Delete Task
|
||||
*/
|
||||
export type deleteTaskTasksTaskIdDeleteResponse200 = {
|
||||
data: unknown;
|
||||
status: 200;
|
||||
};
|
||||
|
||||
export type deleteTaskTasksTaskIdDeleteResponse422 = {
|
||||
data: HTTPValidationError;
|
||||
status: 422;
|
||||
};
|
||||
|
||||
export type deleteTaskTasksTaskIdDeleteResponseSuccess =
|
||||
deleteTaskTasksTaskIdDeleteResponse200 & {
|
||||
headers: Headers;
|
||||
};
|
||||
export type deleteTaskTasksTaskIdDeleteResponseError =
|
||||
deleteTaskTasksTaskIdDeleteResponse422 & {
|
||||
headers: Headers;
|
||||
};
|
||||
|
||||
export type deleteTaskTasksTaskIdDeleteResponse =
|
||||
| deleteTaskTasksTaskIdDeleteResponseSuccess
|
||||
| deleteTaskTasksTaskIdDeleteResponseError;
|
||||
|
||||
export const getDeleteTaskTasksTaskIdDeleteUrl = (taskId: number) => {
|
||||
return `/tasks/${taskId}`;
|
||||
};
|
||||
|
||||
export const deleteTaskTasksTaskIdDelete = async (
|
||||
taskId: number,
|
||||
options?: RequestInit,
|
||||
): Promise<deleteTaskTasksTaskIdDeleteResponse> => {
|
||||
return customFetch<deleteTaskTasksTaskIdDeleteResponse>(
|
||||
getDeleteTaskTasksTaskIdDeleteUrl(taskId),
|
||||
{
|
||||
...options,
|
||||
method: "DELETE",
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* @summary List Task Comments
|
||||
*/
|
||||
export type listTaskCommentsTaskCommentsGetResponse200 = {
|
||||
data: TaskComment[];
|
||||
status: 200;
|
||||
};
|
||||
|
||||
export type listTaskCommentsTaskCommentsGetResponse422 = {
|
||||
data: HTTPValidationError;
|
||||
status: 422;
|
||||
};
|
||||
|
||||
export type listTaskCommentsTaskCommentsGetResponseSuccess =
|
||||
listTaskCommentsTaskCommentsGetResponse200 & {
|
||||
headers: Headers;
|
||||
};
|
||||
export type listTaskCommentsTaskCommentsGetResponseError =
|
||||
listTaskCommentsTaskCommentsGetResponse422 & {
|
||||
headers: Headers;
|
||||
};
|
||||
|
||||
export type listTaskCommentsTaskCommentsGetResponse =
|
||||
| listTaskCommentsTaskCommentsGetResponseSuccess
|
||||
| listTaskCommentsTaskCommentsGetResponseError;
|
||||
|
||||
export const getListTaskCommentsTaskCommentsGetUrl = (
|
||||
params: ListTaskCommentsTaskCommentsGetParams,
|
||||
) => {
|
||||
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
|
||||
? `/task-comments?${stringifiedParams}`
|
||||
: `/task-comments`;
|
||||
};
|
||||
|
||||
export const listTaskCommentsTaskCommentsGet = async (
|
||||
params: ListTaskCommentsTaskCommentsGetParams,
|
||||
options?: RequestInit,
|
||||
): Promise<listTaskCommentsTaskCommentsGetResponse> => {
|
||||
return customFetch<listTaskCommentsTaskCommentsGetResponse>(
|
||||
getListTaskCommentsTaskCommentsGetUrl(params),
|
||||
{
|
||||
...options,
|
||||
method: "GET",
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* @summary Create Task Comment
|
||||
*/
|
||||
export type createTaskCommentTaskCommentsPostResponse200 = {
|
||||
data: TaskComment;
|
||||
status: 200;
|
||||
};
|
||||
|
||||
export type createTaskCommentTaskCommentsPostResponse422 = {
|
||||
data: HTTPValidationError;
|
||||
status: 422;
|
||||
};
|
||||
|
||||
export type createTaskCommentTaskCommentsPostResponseSuccess =
|
||||
createTaskCommentTaskCommentsPostResponse200 & {
|
||||
headers: Headers;
|
||||
};
|
||||
export type createTaskCommentTaskCommentsPostResponseError =
|
||||
createTaskCommentTaskCommentsPostResponse422 & {
|
||||
headers: Headers;
|
||||
};
|
||||
|
||||
export type createTaskCommentTaskCommentsPostResponse =
|
||||
| createTaskCommentTaskCommentsPostResponseSuccess
|
||||
| createTaskCommentTaskCommentsPostResponseError;
|
||||
|
||||
export const getCreateTaskCommentTaskCommentsPostUrl = () => {
|
||||
return `/task-comments`;
|
||||
};
|
||||
|
||||
export const createTaskCommentTaskCommentsPost = async (
|
||||
taskCommentCreate: TaskCommentCreate,
|
||||
options?: RequestInit,
|
||||
): Promise<createTaskCommentTaskCommentsPostResponse> => {
|
||||
return customFetch<createTaskCommentTaskCommentsPostResponse>(
|
||||
getCreateTaskCommentTaskCommentsPostUrl(),
|
||||
{
|
||||
...options,
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json", ...options?.headers },
|
||||
body: JSON.stringify(taskCommentCreate),
|
||||
},
|
||||
);
|
||||
};
|
||||
22
frontend/src/api/mutator.ts
Normal file
22
frontend/src/api/mutator.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
export async function customFetch<T>(
|
||||
url: string,
|
||||
options: RequestInit,
|
||||
): Promise<T> {
|
||||
const base = process.env.NEXT_PUBLIC_API_URL;
|
||||
if (!base) throw new Error("NEXT_PUBLIC_API_URL is not set");
|
||||
|
||||
const res = await fetch(`${base}${url}`, {
|
||||
...options,
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
...(options.headers ?? {}),
|
||||
},
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
const text = await res.text().catch(() => "");
|
||||
throw new Error(`${res.status} ${res.statusText}${text ? `: ${text}` : ""}`);
|
||||
}
|
||||
|
||||
return (await res.json()) as T;
|
||||
}
|
||||
@@ -1,13 +1,67 @@
|
||||
:root{
|
||||
--mc-bg:#f6f4ef;
|
||||
--mc-surface:#ffffff;
|
||||
--mc-border:rgba(15,23,42,0.10);
|
||||
--mc-text:#0f172a;
|
||||
--mc-muted:rgba(15,23,42,0.62);
|
||||
--mc-accent:#2563eb;
|
||||
--mc-font-serif: ui-serif, Georgia, Cambria, "Times New Roman", Times, serif;
|
||||
--mc-font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
:root {
|
||||
--background: 0 0% 100%;
|
||||
--foreground: 222.2 84% 4.9%;
|
||||
|
||||
--card: 0 0% 100%;
|
||||
--card-foreground: 222.2 84% 4.9%;
|
||||
|
||||
--muted: 210 40% 96.1%;
|
||||
--muted-foreground: 215.4 16.3% 46.9%;
|
||||
|
||||
--accent: 210 40% 96.1%;
|
||||
--accent-foreground: 222.2 47.4% 11.2%;
|
||||
|
||||
--border: 214.3 31.8% 91.4%;
|
||||
--input: 214.3 31.8% 91.4%;
|
||||
--ring: 222.2 84% 4.9%;
|
||||
|
||||
--primary: 222.2 47.4% 11.2%;
|
||||
--primary-foreground: 210 40% 98%;
|
||||
|
||||
--secondary: 210 40% 96.1%;
|
||||
--secondary-foreground: 222.2 47.4% 11.2%;
|
||||
|
||||
--destructive: 0 84.2% 60.2%;
|
||||
--destructive-foreground: 210 40% 98%;
|
||||
|
||||
--radius: 12px;
|
||||
}
|
||||
|
||||
.dark {
|
||||
--background: 222.2 84% 4.9%;
|
||||
--foreground: 210 40% 98%;
|
||||
|
||||
--card: 222.2 84% 4.9%;
|
||||
--card-foreground: 210 40% 98%;
|
||||
|
||||
--muted: 217.2 32.6% 17.5%;
|
||||
--muted-foreground: 215 20.2% 65.1%;
|
||||
|
||||
--accent: 217.2 32.6% 17.5%;
|
||||
--accent-foreground: 210 40% 98%;
|
||||
|
||||
--border: 217.2 32.6% 17.5%;
|
||||
--input: 217.2 32.6% 17.5%;
|
||||
--ring: 212.7 26.8% 83.9%;
|
||||
|
||||
--primary: 210 40% 98%;
|
||||
--primary-foreground: 222.2 47.4% 11.2%;
|
||||
|
||||
--secondary: 217.2 32.6% 17.5%;
|
||||
--secondary-foreground: 210 40% 98%;
|
||||
|
||||
--destructive: 0 62.8% 30.6%;
|
||||
--destructive-foreground: 210 40% 98%;
|
||||
}
|
||||
|
||||
* {
|
||||
@apply border-border;
|
||||
}
|
||||
|
||||
body {
|
||||
@apply bg-background text-foreground;
|
||||
}
|
||||
html,body{height:100%}
|
||||
body{margin:0;color:var(--mc-text);background:var(--mc-bg);font-family: ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, "Apple Color Emoji","Segoe UI Emoji";}
|
||||
*{box-sizing:border-box}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { Metadata } from "next";
|
||||
import "./globals.css";
|
||||
import { Providers } from "./providers";
|
||||
import { Shell } from "./_components/Shell";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
@@ -15,7 +16,7 @@ export default function RootLayout({
|
||||
return (
|
||||
<html lang="en">
|
||||
<body>
|
||||
<Shell>{children}</Shell>
|
||||
<Shell><Providers>{children}</Providers></Shell>
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
|
||||
@@ -1,146 +1,68 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
import styles from "./_components/Shell.module.css";
|
||||
import { apiGet } from "../lib/api";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
|
||||
type Activity = {
|
||||
id: number;
|
||||
actor_employee_id: number | null;
|
||||
entity_type: string;
|
||||
entity_id: number | null;
|
||||
verb: string;
|
||||
payload: any;
|
||||
created_at: string;
|
||||
};
|
||||
import { useListProjectsProjectsGet } from "@/api/generated/projects/projects";
|
||||
|
||||
type Project = { id: number; name: string; status: string };
|
||||
|
||||
type Department = { id: number; name: string; head_employee_id: number | null };
|
||||
|
||||
type Employee = {
|
||||
id: number;
|
||||
name: string;
|
||||
employee_type: string;
|
||||
department_id: number | null;
|
||||
manager_id: number | null;
|
||||
title: string | null;
|
||||
status: string;
|
||||
};
|
||||
|
||||
type Task = {
|
||||
id: number;
|
||||
project_id: number;
|
||||
title: string;
|
||||
status: string;
|
||||
assignee_employee_id: number | null;
|
||||
reviewer_employee_id: number | null;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
};
|
||||
|
||||
export default function MissionControlHome() {
|
||||
const [activities, setActivities] = useState<Activity[]>([]);
|
||||
const [projects, setProjects] = useState<Project[]>([]);
|
||||
const [departments, setDepartments] = useState<Department[]>([]);
|
||||
const [employees, setEmployees] = useState<Employee[]>([]);
|
||||
const [tasks, setTasks] = useState<Task[]>([]);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
async function load() {
|
||||
setError(null);
|
||||
try {
|
||||
const [a, p, d, e, t] = await Promise.all([
|
||||
apiGet<Activity[]>("/activities?limit=20"),
|
||||
apiGet<Project[]>("/projects"),
|
||||
apiGet<Department[]>("/departments"),
|
||||
apiGet<Employee[]>("/employees"),
|
||||
apiGet<Task[]>("/tasks"),
|
||||
]);
|
||||
setActivities(a);
|
||||
setProjects(p);
|
||||
setDepartments(d);
|
||||
setEmployees(e);
|
||||
setTasks(t);
|
||||
} catch (err: unknown) {
|
||||
setError(err instanceof Error ? err.message : "Unknown error");
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
load();
|
||||
}, []);
|
||||
|
||||
const activeProjects = projects.filter((x) => x.status === "active").length;
|
||||
const activeEmployees = employees.filter((x) => x.status === "active").length;
|
||||
const blockedTasks = tasks.filter((t) => t.status === "blocked").length;
|
||||
const reviewQueue = tasks.filter((t) => t.status === "review").length;
|
||||
export default function Home() {
|
||||
const projects = useListProjectsProjectsGet();
|
||||
|
||||
return (
|
||||
<main>
|
||||
<div className={styles.topbar}>
|
||||
<main className="mx-auto max-w-5xl p-6">
|
||||
<div className="flex items-start justify-between gap-4">
|
||||
<div>
|
||||
<h1 className={styles.h1}>Mission Control</h1>
|
||||
<p className={styles.p}>
|
||||
Company dashboard: departments, employees/agents, projects, and work — designed to run like a real org.
|
||||
<h1 className="text-3xl font-semibold tracking-tight">Company Mission Control</h1>
|
||||
<p className="mt-2 text-sm text-muted-foreground">
|
||||
Orval-generated client + React Query + shadcn-style components.
|
||||
</p>
|
||||
</div>
|
||||
<button className={styles.btn} onClick={load}>
|
||||
<Button variant="outline" onClick={() => projects.refetch()} disabled={projects.isFetching}>
|
||||
Refresh
|
||||
</button>
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{error ? (
|
||||
<div className={styles.card} style={{ borderColor: "rgba(176,0,32,0.25)" }}>
|
||||
<div className={styles.cardTitle}>Error</div>
|
||||
<div style={{ color: "#b00020" }}>{error}</div>
|
||||
</div>
|
||||
) : null}
|
||||
<div className="mt-6 grid gap-4 sm:grid-cols-2">
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Projects</CardTitle>
|
||||
<CardDescription>GET /projects</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
{projects.isLoading ? <div className="text-sm text-muted-foreground">Loading…</div> : null}
|
||||
{projects.error ? (
|
||||
<div className="text-sm text-destructive">{(projects.error as Error).message}</div>
|
||||
) : null}
|
||||
{!projects.isLoading && !projects.error ? (
|
||||
<ul className="space-y-2">
|
||||
{projects.data?.map((p) => (
|
||||
<li key={p.id ?? p.name} className="flex items-center justify-between rounded-md border p-3">
|
||||
<div className="font-medium">{p.name}</div>
|
||||
<div className="text-xs text-muted-foreground">{p.status}</div>
|
||||
</li>
|
||||
))}
|
||||
{(projects.data?.length ?? 0) === 0 ? (
|
||||
<li className="text-sm text-muted-foreground">No projects yet.</li>
|
||||
) : null}
|
||||
</ul>
|
||||
) : null}
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<div className={styles.grid2} style={{ marginTop: 16 }}>
|
||||
<section className={styles.card}>
|
||||
<div className={styles.cardTitle}>Company Snapshot</div>
|
||||
<div style={{ display: "flex", gap: 10, flexWrap: "wrap" }}>
|
||||
<span className={styles.badge}>Projects: {activeProjects}</span>
|
||||
<span className={styles.badge}>Departments: {departments.length}</span>
|
||||
<span className={styles.badge}>Active people: {activeEmployees}</span>
|
||||
<span className={styles.badge}>In review: {reviewQueue}</span>
|
||||
<span className={styles.badge}>Blocked: {blockedTasks}</span>
|
||||
</div>
|
||||
<div className={styles.list} style={{ marginTop: 12 }}>
|
||||
{projects.slice(0, 6).map((p) => (
|
||||
<div key={p.id} className={styles.item}>
|
||||
<div style={{ display: "flex", justifyContent: "space-between", gap: 12 }}>
|
||||
<div style={{ fontWeight: 650 }}>{p.name}</div>
|
||||
<span className={styles.badge}>{p.status}</span>
|
||||
</div>
|
||||
<div className={styles.mono} style={{ marginTop: 6 }}>
|
||||
Project ID: {p.id}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
{projects.length === 0 ? <div className={styles.mono}>No projects yet. Create one in Projects.</div> : null}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className={styles.card}>
|
||||
<div className={styles.cardTitle}>Activity Feed</div>
|
||||
<div className={styles.list}>
|
||||
{activities.map((a) => (
|
||||
<div key={a.id} className={styles.item}>
|
||||
<div style={{ display: "flex", justifyContent: "space-between", gap: 12 }}>
|
||||
<div>
|
||||
<span style={{ fontWeight: 650 }}>{a.entity_type}</span> · {a.verb}
|
||||
{a.entity_id != null ? ` #${a.entity_id}` : ""}
|
||||
</div>
|
||||
<span className={styles.mono}>{new Date(a.created_at).toLocaleString()}</span>
|
||||
</div>
|
||||
{a.payload ? <div className={styles.mono} style={{ marginTop: 6 }}>{JSON.stringify(a.payload)}</div> : null}
|
||||
</div>
|
||||
))}
|
||||
{activities.length === 0 ? <div className={styles.mono}>No activity yet.</div> : null}
|
||||
</div>
|
||||
</section>
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>API</CardTitle>
|
||||
<CardDescription>Docs & health</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-2 text-sm">
|
||||
<div>
|
||||
<span className="text-muted-foreground">Docs:</span> <code className="ml-2">/docs</code>
|
||||
</div>
|
||||
<div className="text-muted-foreground">
|
||||
Set <code>NEXT_PUBLIC_API_URL</code> in <code>.env.local</code> (example: http://192.168.1.101:8000).
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
|
||||
20
frontend/src/app/providers.tsx
Normal file
20
frontend/src/app/providers.tsx
Normal file
@@ -0,0 +1,20 @@
|
||||
"use client";
|
||||
|
||||
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
||||
import { useState } from "react";
|
||||
|
||||
export function Providers({ children }: { children: React.ReactNode }) {
|
||||
const [client] = useState(
|
||||
() =>
|
||||
new QueryClient({
|
||||
defaultOptions: {
|
||||
queries: {
|
||||
retry: 1,
|
||||
refetchOnWindowFocus: false,
|
||||
},
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
return <QueryClientProvider client={client}>{children}</QueryClientProvider>;
|
||||
}
|
||||
49
frontend/src/components/ui/button.tsx
Normal file
49
frontend/src/components/ui/button.tsx
Normal file
@@ -0,0 +1,49 @@
|
||||
import * as React from "react";
|
||||
import { cva, type VariantProps } from "class-variance-authority";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const buttonVariants = cva(
|
||||
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default: "bg-primary text-primary-foreground hover:opacity-90",
|
||||
secondary: "bg-secondary text-secondary-foreground hover:opacity-90",
|
||||
outline: "border border-input bg-background hover:bg-accent hover:text-accent-foreground",
|
||||
destructive: "bg-destructive text-destructive-foreground hover:opacity-90",
|
||||
ghost: "hover:bg-accent hover:text-accent-foreground",
|
||||
link: "text-primary underline-offset-4 hover:underline",
|
||||
},
|
||||
size: {
|
||||
default: "h-10 px-4 py-2",
|
||||
sm: "h-9 rounded-md px-3",
|
||||
lg: "h-11 rounded-md px-8",
|
||||
icon: "h-10 w-10",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: "default",
|
||||
size: "default",
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
export interface ButtonProps
|
||||
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
|
||||
VariantProps<typeof buttonVariants> {}
|
||||
|
||||
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
||||
({ className, variant, size, ...props }, ref) => {
|
||||
return (
|
||||
<button
|
||||
className={cn(buttonVariants({ variant, size, className }))}
|
||||
ref={ref}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
);
|
||||
Button.displayName = "Button";
|
||||
|
||||
export { Button, buttonVariants };
|
||||
51
frontend/src/components/ui/card.tsx
Normal file
51
frontend/src/components/ui/card.tsx
Normal file
@@ -0,0 +1,51 @@
|
||||
import * as React from "react";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const Card = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
|
||||
({ className, ...props }, ref) => (
|
||||
<div
|
||||
ref={ref}
|
||||
className={cn("rounded-lg border bg-card text-card-foreground shadow-sm", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
);
|
||||
Card.displayName = "Card";
|
||||
|
||||
const CardHeader = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
|
||||
({ className, ...props }, ref) => (
|
||||
<div ref={ref} className={cn("flex flex-col space-y-1.5 p-6", className)} {...props} />
|
||||
)
|
||||
);
|
||||
CardHeader.displayName = "CardHeader";
|
||||
|
||||
const CardTitle = React.forwardRef<HTMLParagraphElement, React.HTMLAttributes<HTMLHeadingElement>>(
|
||||
({ className, ...props }, ref) => (
|
||||
<h3 ref={ref} className={cn("text-2xl font-semibold leading-none tracking-tight", className)} {...props} />
|
||||
)
|
||||
);
|
||||
CardTitle.displayName = "CardTitle";
|
||||
|
||||
const CardDescription = React.forwardRef<HTMLParagraphElement, React.HTMLAttributes<HTMLParagraphElement>>(
|
||||
({ className, ...props }, ref) => (
|
||||
<p ref={ref} className={cn("text-sm text-muted-foreground", className)} {...props} />
|
||||
)
|
||||
);
|
||||
CardDescription.displayName = "CardDescription";
|
||||
|
||||
const CardContent = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
|
||||
({ className, ...props }, ref) => (
|
||||
<div ref={ref} className={cn("p-6 pt-0", className)} {...props} />
|
||||
)
|
||||
);
|
||||
CardContent.displayName = "CardContent";
|
||||
|
||||
const CardFooter = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
|
||||
({ className, ...props }, ref) => (
|
||||
<div ref={ref} className={cn("flex items-center p-6 pt-0", className)} {...props} />
|
||||
)
|
||||
);
|
||||
CardFooter.displayName = "CardFooter";
|
||||
|
||||
export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent };
|
||||
6
frontend/src/lib/utils.ts
Normal file
6
frontend/src/lib/utils.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import { clsx, type ClassValue } from "clsx";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
|
||||
export function cn(...inputs: ClassValue[]) {
|
||||
return twMerge(clsx(inputs));
|
||||
}
|
||||
50
frontend/tailwind.config.cjs
Normal file
50
frontend/tailwind.config.cjs
Normal file
@@ -0,0 +1,50 @@
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
module.exports = {
|
||||
darkMode: ["class"],
|
||||
content: [
|
||||
"./src/pages/**/*.{js,ts,jsx,tsx,mdx}",
|
||||
"./src/components/**/*.{js,ts,jsx,tsx,mdx}",
|
||||
"./src/app/**/*.{js,ts,jsx,tsx,mdx}",
|
||||
],
|
||||
theme: {
|
||||
extend: {
|
||||
borderRadius: {
|
||||
lg: "var(--radius)",
|
||||
md: "calc(var(--radius) - 2px)",
|
||||
sm: "calc(var(--radius) - 4px)",
|
||||
},
|
||||
colors: {
|
||||
border: "hsl(var(--border))",
|
||||
input: "hsl(var(--input))",
|
||||
ring: "hsl(var(--ring))",
|
||||
background: "hsl(var(--background))",
|
||||
foreground: "hsl(var(--foreground))",
|
||||
primary: {
|
||||
DEFAULT: "hsl(var(--primary))",
|
||||
foreground: "hsl(var(--primary-foreground))",
|
||||
},
|
||||
secondary: {
|
||||
DEFAULT: "hsl(var(--secondary))",
|
||||
foreground: "hsl(var(--secondary-foreground))",
|
||||
},
|
||||
muted: {
|
||||
DEFAULT: "hsl(var(--muted))",
|
||||
foreground: "hsl(var(--muted-foreground))",
|
||||
},
|
||||
accent: {
|
||||
DEFAULT: "hsl(var(--accent))",
|
||||
foreground: "hsl(var(--accent-foreground))",
|
||||
},
|
||||
destructive: {
|
||||
DEFAULT: "hsl(var(--destructive))",
|
||||
foreground: "hsl(var(--destructive-foreground))",
|
||||
},
|
||||
card: {
|
||||
DEFAULT: "hsl(var(--card))",
|
||||
foreground: "hsl(var(--card-foreground))",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
plugins: [require("tailwindcss-animate")],
|
||||
};
|
||||
@@ -11,24 +11,13 @@
|
||||
"moduleResolution": "bundler",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"jsx": "react-jsx",
|
||||
"jsx": "preserve",
|
||||
"incremental": true,
|
||||
"plugins": [
|
||||
{
|
||||
"name": "next"
|
||||
}
|
||||
],
|
||||
"plugins": [{ "name": "next" }],
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
}
|
||||
},
|
||||
"include": [
|
||||
"next-env.d.ts",
|
||||
"**/*.ts",
|
||||
"**/*.tsx",
|
||||
".next/types/**/*.ts",
|
||||
".next/dev/types/**/*.ts",
|
||||
"**/*.mts"
|
||||
],
|
||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user