diff --git a/.gitignore b/.gitignore index 2c9b447c..261aed6d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,20 +1,15 @@ -# OS / IDE -.idea/ -.DS_Store - # Python -backend/.venv/ -backend/__pycache__/ -backend/.pytest_cache/ -backend/.env +__pycache__/ +*.py[cod] -# Node -frontend/node_modules/ -frontend/.next/ -frontend/.env.local +# Node / Next +node_modules/ +.next/ -# Logs -*.log +# Env +.env +.env.local -# Local run logs +# IDE +.idea/ .runlogs/ diff --git a/backend/.gitignore b/backend/.gitignore new file mode 100644 index 00000000..3d6a54ba --- /dev/null +++ b/backend/.gitignore @@ -0,0 +1,4 @@ +__pycache__/ +*.py[cod] +.env +.venv/ diff --git a/backend/app/api/__pycache__/activities.cpython-312.pyc b/backend/app/api/__pycache__/activities.cpython-312.pyc deleted file mode 100644 index a3e601ce..00000000 Binary files a/backend/app/api/__pycache__/activities.cpython-312.pyc and /dev/null differ diff --git a/backend/app/api/__pycache__/hr.cpython-312.pyc b/backend/app/api/__pycache__/hr.cpython-312.pyc deleted file mode 100644 index 7d8b6a85..00000000 Binary files a/backend/app/api/__pycache__/hr.cpython-312.pyc and /dev/null differ diff --git a/backend/app/api/__pycache__/org.cpython-312.pyc b/backend/app/api/__pycache__/org.cpython-312.pyc deleted file mode 100644 index f6ebdac5..00000000 Binary files a/backend/app/api/__pycache__/org.cpython-312.pyc and /dev/null differ diff --git a/backend/app/api/__pycache__/projects.cpython-312.pyc b/backend/app/api/__pycache__/projects.cpython-312.pyc deleted file mode 100644 index 10d14ecc..00000000 Binary files a/backend/app/api/__pycache__/projects.cpython-312.pyc and /dev/null differ diff --git a/backend/app/api/__pycache__/utils.cpython-312.pyc b/backend/app/api/__pycache__/utils.cpython-312.pyc deleted file mode 100644 index 8880b641..00000000 Binary files a/backend/app/api/__pycache__/utils.cpython-312.pyc and /dev/null differ diff --git a/backend/app/api/__pycache__/work.cpython-312.pyc b/backend/app/api/__pycache__/work.cpython-312.pyc deleted file mode 100644 index e81425b0..00000000 Binary files a/backend/app/api/__pycache__/work.cpython-312.pyc and /dev/null differ diff --git a/backend/app/models/__pycache__/__init__.cpython-312.pyc b/backend/app/models/__pycache__/__init__.cpython-312.pyc deleted file mode 100644 index 50b46221..00000000 Binary files a/backend/app/models/__pycache__/__init__.cpython-312.pyc and /dev/null differ diff --git a/backend/app/models/__pycache__/activity.cpython-312.pyc b/backend/app/models/__pycache__/activity.cpython-312.pyc deleted file mode 100644 index d412768e..00000000 Binary files a/backend/app/models/__pycache__/activity.cpython-312.pyc and /dev/null differ diff --git a/backend/app/models/__pycache__/hr.cpython-312.pyc b/backend/app/models/__pycache__/hr.cpython-312.pyc deleted file mode 100644 index bfa75cbf..00000000 Binary files a/backend/app/models/__pycache__/hr.cpython-312.pyc and /dev/null differ diff --git a/backend/app/models/__pycache__/org.cpython-312.pyc b/backend/app/models/__pycache__/org.cpython-312.pyc deleted file mode 100644 index fd18fac2..00000000 Binary files a/backend/app/models/__pycache__/org.cpython-312.pyc and /dev/null differ diff --git a/backend/app/models/__pycache__/projects.cpython-312.pyc b/backend/app/models/__pycache__/projects.cpython-312.pyc deleted file mode 100644 index ab7a3390..00000000 Binary files a/backend/app/models/__pycache__/projects.cpython-312.pyc and /dev/null differ diff --git a/backend/app/models/__pycache__/work.cpython-312.pyc b/backend/app/models/__pycache__/work.cpython-312.pyc deleted file mode 100644 index 532721b8..00000000 Binary files a/backend/app/models/__pycache__/work.cpython-312.pyc and /dev/null differ diff --git a/backend/app/schemas/__pycache__/hr.cpython-312.pyc b/backend/app/schemas/__pycache__/hr.cpython-312.pyc deleted file mode 100644 index c8957d29..00000000 Binary files a/backend/app/schemas/__pycache__/hr.cpython-312.pyc and /dev/null differ diff --git a/backend/app/schemas/__pycache__/org.cpython-312.pyc b/backend/app/schemas/__pycache__/org.cpython-312.pyc deleted file mode 100644 index 40fca14f..00000000 Binary files a/backend/app/schemas/__pycache__/org.cpython-312.pyc and /dev/null differ diff --git a/backend/app/schemas/__pycache__/projects.cpython-312.pyc b/backend/app/schemas/__pycache__/projects.cpython-312.pyc deleted file mode 100644 index d9fc2785..00000000 Binary files a/backend/app/schemas/__pycache__/projects.cpython-312.pyc and /dev/null differ diff --git a/backend/app/schemas/__pycache__/work.cpython-312.pyc b/backend/app/schemas/__pycache__/work.cpython-312.pyc deleted file mode 100644 index ddbdc63a..00000000 Binary files a/backend/app/schemas/__pycache__/work.cpython-312.pyc and /dev/null differ diff --git a/frontend/src/app/departments/page.tsx b/frontend/src/app/departments/page.tsx index 75ba167d..d4744dd9 100644 --- a/frontend/src/app/departments/page.tsx +++ b/frontend/src/app/departments/page.tsx @@ -5,7 +5,7 @@ import { useState } from "react"; import { Button } from "@/components/ui/button"; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; import { Input } from "@/components/ui/input"; -import { normalizeDepartments, normalizeEmployees } from "@/lib/normalize"; + import { Select } from "@/components/ui/select"; import { @@ -20,10 +20,10 @@ export default function DepartmentsPage() { const [headId, setHeadId] = useState(""); const departments = useListDepartmentsDepartmentsGet(); - const departmentList = normalizeDepartments(departments.data); + const departmentList = departments.data ?? []; const employees = useListEmployeesEmployeesGet(); - const employeeList = normalizeEmployees(employees.data); + const employeeList = employees.data ?? []; const createDepartment = useCreateDepartmentDepartmentsPost({ mutation: { diff --git a/frontend/src/app/hr/page.tsx b/frontend/src/app/hr/page.tsx index 2e49f51b..30805b84 100644 --- a/frontend/src/app/hr/page.tsx +++ b/frontend/src/app/hr/page.tsx @@ -5,7 +5,7 @@ import { useState } from "react"; import { Button } from "@/components/ui/button"; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; import { Input } from "@/components/ui/input"; -import { normalizeAgentOnboardings, normalizeDepartments, normalizeEmployees, normalizeEmploymentActions, normalizeHeadcountRequests } from "@/lib/normalize"; + import { Select } from "@/components/ui/select"; import { Textarea } from "@/components/ui/textarea"; @@ -22,16 +22,16 @@ import { useListDepartmentsDepartmentsGet, useListEmployeesEmployeesGet } from " export default function HRPage() { const departments = useListDepartmentsDepartmentsGet(); - const departmentList = normalizeDepartments(departments.data); + const departmentList = departments.data ?? []; const employees = useListEmployeesEmployeesGet(); - const employeeList = normalizeEmployees(employees.data); + const employeeList = employees.data ?? []; const headcount = useListHeadcountRequestsHrHeadcountGet(); const actions = useListEmploymentActionsHrActionsGet(); const onboarding = useListAgentOnboardingHrOnboardingGet(); - const headcountList = normalizeHeadcountRequests(headcount.data); - const actionList = normalizeEmploymentActions(actions.data); - const onboardingList = normalizeAgentOnboardings(onboarding.data); + const headcountList = headcount.data ?? []; + const actionList = actions.data ?? []; + const onboardingList = onboarding.data ?? []; const [hcDeptId, setHcDeptId] = useState(""); const [hcManagerId, setHcManagerId] = useState(""); diff --git a/frontend/src/app/page.tsx b/frontend/src/app/page.tsx index 1766ba26..a34ed799 100644 --- a/frontend/src/app/page.tsx +++ b/frontend/src/app/page.tsx @@ -6,7 +6,7 @@ import styles from "@/app/_components/Shell.module.css"; import { Button } from "@/components/ui/button"; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; import { Input } from "@/components/ui/input"; -import { normalizeActivities, normalizeDepartments, normalizeEmployees, normalizeProjects } from "@/lib/normalize"; +import { normalizeActivities } from "@/lib/normalize"; import { Select } from "@/components/ui/select"; import { useCreateProjectProjectsPost, useListProjectsProjectsGet } from "@/api/generated/projects/projects"; @@ -16,12 +16,12 @@ import { useListActivitiesActivitiesGet } from "@/api/generated/activities/activ export default function Home() { const projects = useListProjectsProjectsGet(); - const projectList = normalizeProjects(projects.data); + const projectList = projects.data ?? []; const departments = useListDepartmentsDepartmentsGet(); - const departmentList = normalizeDepartments(departments.data); + const departmentList = departments.data ?? []; const employees = useListEmployeesEmployeesGet(); const activities = useListActivitiesActivitiesGet({ limit: 20 }); - const employeeList = normalizeEmployees(employees.data); + const employeeList = employees.data ?? []; const activityList = normalizeActivities(activities.data); const [projectName, setProjectName] = useState(""); diff --git a/frontend/src/app/people/page.tsx b/frontend/src/app/people/page.tsx index 10cb1d29..e0c06d83 100644 --- a/frontend/src/app/people/page.tsx +++ b/frontend/src/app/people/page.tsx @@ -6,7 +6,7 @@ import { Button } from "@/components/ui/button"; import { Badge } from "@/components/ui/badge"; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; import { Input } from "@/components/ui/input"; -import { normalizeDepartments, normalizeEmployees } from "@/lib/normalize"; + import { Select } from "@/components/ui/select"; import { @@ -24,8 +24,8 @@ export default function PeoplePage() { const employees = useListEmployeesEmployeesGet(); const departments = useListDepartmentsDepartmentsGet(); - const departmentList = normalizeDepartments(departments.data); - const employeeList = normalizeEmployees(employees.data); + const departmentList = departments.data ?? []; + const employeeList = employees.data ?? []; const createEmployee = useCreateEmployeeEmployeesPost({ mutation: { diff --git a/frontend/src/app/projects/[id]/page.tsx b/frontend/src/app/projects/[id]/page.tsx index 723c78fa..78326342 100644 --- a/frontend/src/app/projects/[id]/page.tsx +++ b/frontend/src/app/projects/[id]/page.tsx @@ -6,7 +6,7 @@ import { useParams } from "next/navigation"; import { Button } from "@/components/ui/button"; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; import { Input } from "@/components/ui/input"; -import { normalizeEmployees, normalizeProjectMembers, normalizeProjects, normalizeTaskComments, normalizeTasks } from "@/lib/normalize"; + import { Select } from "@/components/ui/select"; import { Textarea } from "@/components/ui/textarea"; @@ -34,14 +34,14 @@ export default function ProjectDetailPage() { const projectId = Number(params?.id); const projects = useListProjectsProjectsGet(); - const projectList = normalizeProjects(projects.data); + const projectList = projects.data ?? []; const project = projectList.find((p) => p.id === projectId); const employees = useListEmployeesEmployeesGet(); - const employeeList = normalizeEmployees(employees.data); + const employeeList = employees.data ?? []; const members = useListProjectMembersProjectsProjectIdMembersGet(projectId); - const memberList = normalizeProjectMembers(members.data); + const memberList = members.data ?? []; const addMember = useAddProjectMemberProjectsProjectIdMembersPost({ mutation: { onSuccess: () => members.refetch() }, }); @@ -53,7 +53,7 @@ export default function ProjectDetailPage() { }); const tasks = useListTasksTasksGet({ project_id: projectId }); - const taskList = normalizeTasks(tasks.data); + const taskList = tasks.data ?? []; const createTask = useCreateTaskTasksPost({ mutation: { onSuccess: () => tasks.refetch() }, }); @@ -76,7 +76,7 @@ export default function ProjectDetailPage() { { task_id: commentTaskId ?? 0 }, { query: { enabled: Boolean(commentTaskId) } }, ); - const commentList = normalizeTaskComments(comments.data); + const commentList = comments.data ?? []; const addComment = useCreateTaskCommentTaskCommentsPost({ mutation: { onSuccess: () => { diff --git a/frontend/src/app/projects/page.tsx b/frontend/src/app/projects/page.tsx index e47c5392..c97a5148 100644 --- a/frontend/src/app/projects/page.tsx +++ b/frontend/src/app/projects/page.tsx @@ -7,7 +7,6 @@ import styles from "@/app/_components/Shell.module.css"; import { Button } from "@/components/ui/button"; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; import { Input } from "@/components/ui/input"; -import { normalizeProjects } from "@/lib/normalize"; import { useCreateProjectProjectsPost, @@ -18,7 +17,7 @@ export default function ProjectsPage() { const [name, setName] = useState(""); const projects = useListProjectsProjectsGet(); - const projectList = normalizeProjects(projects.data); + const projectList = projects.data ?? []; const createProject = useCreateProjectProjectsPost({ mutation: { onSuccess: () => { diff --git a/frontend/src/lib/api.ts b/frontend/src/lib/api.ts deleted file mode 100644 index af53c09a..00000000 --- a/frontend/src/lib/api.ts +++ /dev/null @@ -1,24 +0,0 @@ -export function apiUrl(path: string) { - const base = process.env.NEXT_PUBLIC_API_URL; - if (!base) throw new Error("NEXT_PUBLIC_API_URL is not set"); - return `${base}${path}`; -} - -export async function apiGet(path: string): Promise { - const res = await fetch(apiUrl(path), { cache: "no-store" }); - if (!res.ok) throw new Error(`GET ${path} failed (${res.status})`); - return (await res.json()) as T; -} - -export async function apiSend( - path: string, - opts: { method: "POST" | "PATCH" | "DELETE"; body?: unknown } -): Promise { - const res = await fetch(apiUrl(path), { - method: opts.method, - headers: opts.body ? { "Content-Type": "application/json" } : undefined, - body: opts.body ? JSON.stringify(opts.body) : undefined, - }); - if (!res.ok) throw new Error(`${opts.method} ${path} failed (${res.status})`); - return (await res.json()) as T; -} diff --git a/frontend/src/lib/normalize.ts b/frontend/src/lib/normalize.ts index f92bea55..7ddb1b03 100644 --- a/frontend/src/lib/normalize.ts +++ b/frontend/src/lib/normalize.ts @@ -1,6 +1,8 @@ -import type { Department } from "@/api/generated/model/department"; +// NOTE: +// Orval-generated hooks already return strongly-typed arrays for most endpoints. +// We keep only the Activity type + a tiny normalizer here because Activity is not +// currently generated as a model. -// Local activity shape (not generated as a model) export type Activity = { id?: number; actor_employee_id?: number | null; @@ -10,59 +12,6 @@ export type Activity = { payload?: unknown; created_at?: string; }; -import type { Employee } from "@/api/generated/model/employee"; -import type { AgentOnboarding } from "@/api/generated/model/agentOnboarding"; -import type { EmploymentAction } from "@/api/generated/model/employmentAction"; -import type { HeadcountRequest } from "@/api/generated/model/headcountRequest"; -import type { Project } from "@/api/generated/model/project"; -import type { Task } from "@/api/generated/model/task"; -import type { ProjectMember } from "@/api/generated/model/projectMember"; -import type { TaskComment } from "@/api/generated/model/taskComment"; - -export function normalizeEmployees(data: unknown): Employee[] { - if (Array.isArray(data)) return data as Employee[]; - if (data && typeof data === "object" && "data" in data) { - const maybe = (data as { data?: unknown }).data; - if (Array.isArray(maybe)) return maybe as Employee[]; - } - return []; -} - -export function normalizeDepartments(data: unknown): Department[] { - if (Array.isArray(data)) return data as Department[]; - if (data && typeof data === "object" && "data" in data) { - const maybe = (data as { data?: unknown }).data; - if (Array.isArray(maybe)) return maybe as Department[]; - } - return []; -} - -export function normalizeHeadcountRequests(data: unknown): HeadcountRequest[] { - if (Array.isArray(data)) return data as HeadcountRequest[]; - if (data && typeof data === "object" && "data" in data) { - const maybe = (data as { data?: unknown }).data; - if (Array.isArray(maybe)) return maybe as HeadcountRequest[]; - } - return []; -} - -export function normalizeEmploymentActions(data: unknown): EmploymentAction[] { - if (Array.isArray(data)) return data as EmploymentAction[]; - if (data && typeof data === "object" && "data" in data) { - const maybe = (data as { data?: unknown }).data; - if (Array.isArray(maybe)) return maybe as EmploymentAction[]; - } - return []; -} - -export function normalizeAgentOnboardings(data: unknown): AgentOnboarding[] { - if (Array.isArray(data)) return data as AgentOnboarding[]; - if (data && typeof data === "object" && "data" in data) { - const maybe = (data as { data?: unknown }).data; - if (Array.isArray(maybe)) return maybe as AgentOnboarding[]; - } - return []; -} export function normalizeActivities(data: unknown): Activity[] { if (Array.isArray(data)) return data as Activity[]; @@ -72,39 +21,3 @@ export function normalizeActivities(data: unknown): Activity[] { } return []; } - -export function normalizeProjects(data: unknown): Project[] { - if (Array.isArray(data)) return data as Project[]; - if (data && typeof data === "object" && "data" in data) { - const maybe = (data as { data?: unknown }).data; - if (Array.isArray(maybe)) return maybe as Project[]; - } - return []; -} - -export function normalizeTasks(data: unknown): Task[] { - if (Array.isArray(data)) return data as Task[]; - if (data && typeof data === "object" && "data" in data) { - const maybe = (data as { data?: unknown }).data; - if (Array.isArray(maybe)) return maybe as Task[]; - } - return []; -} - -export function normalizeTaskComments(data: unknown): TaskComment[] { - if (Array.isArray(data)) return data as TaskComment[]; - if (data && typeof data === "object" && "data" in data) { - const maybe = (data as { data?: unknown }).data; - if (Array.isArray(maybe)) return maybe as TaskComment[]; - } - return []; -} - -export function normalizeProjectMembers(data: unknown): ProjectMember[] { - if (Array.isArray(data)) return data as ProjectMember[]; - if (data && typeof data === "object" && "data" in data) { - const maybe = (data as { data?: unknown }).data; - if (Array.isArray(maybe)) return maybe as ProjectMember[]; - } - return []; -}