From 7b457244ae5c295bede2e18ef336772a5381229f Mon Sep 17 00:00:00 2001 From: James Ketrenos Date: Mon, 9 Jun 2025 10:59:24 -0700 Subject: [PATCH] Changed header menu Added date fields to Job --- frontend/src/components/JobCreator.tsx | 4 ++- frontend/src/components/layout/Header.tsx | 19 +++++++++--- frontend/src/components/ui/JobInfo.tsx | 2 +- frontend/src/config/navigationConfig.tsx | 4 +-- frontend/src/hooks/AuthContext.tsx | 4 +-- frontend/src/services/api-client.ts | 1 - frontend/src/types/types.ts | 38 +++++++++++++++++------ src/backend/agents/candidate_chat.py | 2 +- src/backend/main.py | 1 + src/backend/models.py | 3 ++ 10 files changed, 57 insertions(+), 21 deletions(-) diff --git a/frontend/src/components/JobCreator.tsx b/frontend/src/components/JobCreator.tsx index f3cd020..8bf8d17 100644 --- a/frontend/src/components/JobCreator.tsx +++ b/frontend/src/components/JobCreator.tsx @@ -328,7 +328,9 @@ const JobCreator = (props: JobCreator) => { company: company, summary: summary, title: jobTitle, - requirements: jobRequirements || undefined + requirements: jobRequirements || undefined, + createdAt: new Date(), + updatedAt: new Date(), }; setIsProcessing(true); const job = await apiClient.createJob(newJob); diff --git a/frontend/src/components/layout/Header.tsx b/frontend/src/components/layout/Header.tsx index 8e81eaf..810d4df 100644 --- a/frontend/src/components/layout/Header.tsx +++ b/frontend/src/components/layout/Header.tsx @@ -26,6 +26,7 @@ import { List, ListItem, ListItemButton, + SxProps, } from '@mui/material'; import { styled, useTheme } from '@mui/material/styles'; import { @@ -256,14 +257,23 @@ const Header: React.FC = (props: HeaderProps) => { // Render desktop navigation with dropdowns const renderDesktopNavigation = () => { return ( - - {navigationItems.map((item) => { + + {navigationItems.map((item, index) => { const hasChildren = item.children && item.children.length > 0; const isActive = isCurrentPath(item) || hasActiveChild(item); - + // Default is centered for all menu items + let sx : SxProps = { justifycontent: "center"}; + // First item ("Backstory") is left aligned + if (index === 0) { + sx = { marginRight: "auto" }; + } + // If there is an Admin menu, it is on the far right + if (item.userTypes?.includes('admin')) { + sx = { marginLeft: "auto"}; + } if (hasChildren) { return ( - + handleDropdownOpen(e, item.id)} endIcon={} @@ -306,6 +316,7 @@ const Header: React.FC = (props: HeaderProps) => { sx={{ backgroundColor: isActive ? 'action.selected' : 'transparent', color: isActive ? 'secondary.main' : 'primary.contrastText', + ...sx }} > {item.icon && {item.icon}} diff --git a/frontend/src/components/ui/JobInfo.tsx b/frontend/src/components/ui/JobInfo.tsx index fe6931e..221b94c 100644 --- a/frontend/src/components/ui/JobInfo.tsx +++ b/frontend/src/components/ui/JobInfo.tsx @@ -45,7 +45,7 @@ const JobInfo: React.FC = (props: JobInfoProps) => { if (!job) { return No user loaded.; } - + console.log(job); return ( (Settings, path: '/', component: , userTypes: ['guest', 'candidate', 'employer'], exact: true, }, - { id: 'chat', label: 'Chat about a Candidate', path: '/chat', icon: , component: , userTypes: ['guest', 'candidate', 'employer',], }, - { + { id: 'job-analysis', label: 'Job Analysis', path: '/job-analysis', icon: , component: , userTypes: ['guest', 'candidate', 'employer',], }, + { id: 'chat', label: 'Candidate Chat', path: '/chat', icon: , component: , userTypes: ['guest', 'candidate', 'employer',], }, { id: 'candidate-menu', label: 'Tools', icon: , userTypes: ['candidate'], children: [ { id: 'candidate-dashboard', label: 'Dashboard', path: '/candidate/dashboard', icon: , component: , userTypes: ['candidate'] }, { id: 'candidate-profile', label: 'Profile', icon: , path: '/candidate/profile', component: , userTypes: ['candidate'] }, diff --git a/frontend/src/hooks/AuthContext.tsx b/frontend/src/hooks/AuthContext.tsx index 51e0568..6bc100b 100644 --- a/frontend/src/hooks/AuthContext.tsx +++ b/frontend/src/hooks/AuthContext.tsx @@ -2,8 +2,8 @@ import React, { createContext, useContext, useState, useCallback, useEffect, useRef } from 'react'; import * as Types from '../types/types'; -import { ApiClient, CreateCandidateRequest, CreateEmployerRequest, GuestConversionRequest } from '../services/api-client'; -import { formatApiRequest, toCamelCase } from '../types/conversion'; +import { ApiClient, CreateCandidateRequest, CreateEmployerRequest, GuestConversionRequest } from 'services/api-client'; +import { formatApiRequest, toCamelCase } from 'types/conversion'; // ============================ // Enhanced Types and Interfaces diff --git a/frontend/src/services/api-client.ts b/frontend/src/services/api-client.ts index 0d5d3cf..be4f70d 100644 --- a/frontend/src/services/api-client.ts +++ b/frontend/src/services/api-client.ts @@ -196,7 +196,6 @@ class ApiClient { const data = await response.json(); const apiResponse = parsePaginatedResponse(data); const extractedData = extractApiData(apiResponse); - console.log("extracted", extractedData); // Apply model-specific date conversion to array items if modelType is provided if (modelType && extractedData.data) { return { diff --git a/frontend/src/types/types.ts b/frontend/src/types/types.ts index 4257d19..652bba6 100644 --- a/frontend/src/types/types.ts +++ b/frontend/src/types/types.ts @@ -1,6 +1,6 @@ // Generated TypeScript types from Pydantic models // Source: src/backend/models.py -// Generated on: 2025-06-09T15:19:57.985888 +// Generated on: 2025-06-09T17:45:24.922154 // DO NOT EDIT MANUALLY - This file is auto-generated // ============================ @@ -426,13 +426,6 @@ export interface ChromaDBGetResponse { umapEmbedding3D?: Array; } -export interface Citation { - text: string; - source: string; - context: string; - relevance: number; -} - export interface CreateCandidateRequest { email: string; username: string; @@ -699,11 +692,14 @@ export interface Job { id?: string; ownerId: string; ownerType: "candidate" | "employer" | "guest"; + owner?: BaseUser; title?: string; summary?: string; company?: string; description: string; requirements?: JobRequirements; + createdAt: Date; + updatedAt: Date; } export interface JobApplication { @@ -726,11 +722,14 @@ export interface JobFull { id?: string; ownerId: string; ownerType: "candidate" | "employer" | "guest"; + owner?: BaseUser; title?: string; summary?: string; company?: string; description: string; requirements?: JobRequirements; + createdAt: Date; + updatedAt: Date; location: Location; salaryRange?: SalaryRange; employmentType: "full-time" | "part-time" | "contract" | "internship" | "freelance"; @@ -1470,6 +1469,21 @@ export function convertInterviewScheduleFromApi(data: any): InterviewSchedule { endDate: new Date(data.endDate), }; } +/** + * Convert Job from API response, parsing date fields + * Date fields: createdAt, updatedAt + */ +export function convertJobFromApi(data: any): Job { + if (!data) return data; + + return { + ...data, + // Convert createdAt from ISO string to Date + createdAt: new Date(data.createdAt), + // Convert updatedAt from ISO string to Date + updatedAt: new Date(data.updatedAt), + }; +} /** * Convert JobApplication from API response, parsing date fields * Date fields: appliedDate, updatedDate @@ -1487,13 +1501,17 @@ export function convertJobApplicationFromApi(data: any): JobApplication { } /** * Convert JobFull from API response, parsing date fields - * Date fields: datePosted, applicationDeadline, featuredUntil + * Date fields: createdAt, updatedAt, datePosted, applicationDeadline, featuredUntil */ export function convertJobFullFromApi(data: any): JobFull { if (!data) return data; return { ...data, + // Convert createdAt from ISO string to Date + createdAt: new Date(data.createdAt), + // Convert updatedAt from ISO string to Date + updatedAt: new Date(data.updatedAt), // Convert datePosted from ISO string to Date datePosted: new Date(data.datePosted), // Convert applicationDeadline from ISO string to Date @@ -1688,6 +1706,8 @@ export function convertFromApi(data: any, modelType: string): T { return convertInterviewFeedbackFromApi(data) as T; case 'InterviewSchedule': return convertInterviewScheduleFromApi(data) as T; + case 'Job': + return convertJobFromApi(data) as T; case 'JobApplication': return convertJobApplicationFromApi(data) as T; case 'JobFull': diff --git a/src/backend/agents/candidate_chat.py b/src/backend/agents/candidate_chat.py index 4c217e6..43872ce 100644 --- a/src/backend/agents/candidate_chat.py +++ b/src/backend/agents/candidate_chat.py @@ -28,7 +28,7 @@ class CandidateChat(Agent): CandidateChat Agent """ - agent_type: Literal["candidate_chat"] = "candidate_chat" + agent_type: Literal["candidate_chat"] = "candidate_chat" # type: ignore _agent_type: ClassVar[str] = agent_type # Add this for registration system_prompt: str = system_message diff --git a/src/backend/main.py b/src/backend/main.py index 51ee49a..f859f7b 100644 --- a/src/backend/main.py +++ b/src/backend/main.py @@ -3236,6 +3236,7 @@ async def create_candidate_job( # Add required fields job.id = str(uuid.uuid4()) job.owner_id = current_user.id + job.owner = current_user await database.set_job(job.id, job.model_dump()) diff --git a/src/backend/models.py b/src/backend/models.py index 502f8a8..3563ea4 100644 --- a/src/backend/models.py +++ b/src/backend/models.py @@ -683,11 +683,14 @@ class Job(BaseModel): id: str = Field(default_factory=lambda: str(uuid.uuid4())) owner_id: str = Field(..., alias="ownerId") owner_type: UserType = Field(..., alias="ownerType") + owner: Optional[BaseUser] = None title: Optional[str] summary: Optional[str] company: Optional[str] description: str requirements: Optional[JobRequirements] + created_at: datetime = Field(..., alias="createdAt") + updated_at: datetime = Field(..., alias="updatedAt") model_config = { "populate_by_name": True # Allow both field names and aliases }