Beta seems functional
This commit is contained in:
parent
74201d0a71
commit
d2d0bb29ac
@ -220,7 +220,7 @@ export const navigationConfig: NavigationConfig = {
|
||||
// },
|
||||
{
|
||||
id: "candidate-settings",
|
||||
label: "Settings",
|
||||
label: "System Information",
|
||||
path: "/candidate/settings",
|
||||
icon: <SettingsIcon />,
|
||||
component: <Settings />,
|
||||
|
@ -307,14 +307,6 @@ const HowItWorks: React.FC = () => {
|
||||
<Typography variant="h5" sx={{ mb: 3, fontWeight: 400 }}>
|
||||
Here are your steps from zero-to-hero to see Backstory in action
|
||||
</Typography>
|
||||
<Chip
|
||||
label="Beta Version"
|
||||
sx={{
|
||||
backgroundColor: 'action.active',
|
||||
color: 'background.paper',
|
||||
fontWeight: 'bold'
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
</Container>
|
||||
</HeroSection>
|
||||
@ -395,7 +387,7 @@ const HowItWorks: React.FC = () => {
|
||||
subtitle="Begin the AI analysis"
|
||||
icon={<PlayArrowIcon sx={{ color: 'action.active' }} />}
|
||||
description={[
|
||||
"After selecting a candidate, you are ready to have Backstory perform the Job Analysis. During this phase, Backstory will take each of requirements that were extracted from the Job and match it against any information available about the selected candidate.",
|
||||
"After selecting a candidate, you are ready to have Backstory perform the Job Analysis. During this phase, Backstory will take each of requirements extracted from the Job and match it against information about the selected candidate.",
|
||||
"This could be as little as a simple resume, or as complete as a full work history. Backstory performs similarity searches to identify key elements from the candidate that pertain to a given skill and provides a graded response.",
|
||||
"To see that in action, click the \"Start Skill Assessment\" button."
|
||||
]}
|
||||
@ -415,7 +407,7 @@ const HowItWorks: React.FC = () => {
|
||||
subtitle="Watch the magic happen"
|
||||
icon={<AutoAwesomeIcon sx={{ color: 'action.active' }} />}
|
||||
description={[
|
||||
"Once you begin that action, the Start Skill Assessment button will grey out and the page will begin updating as it discovers information about the candidate. As it does its thing, you can monitor the progress and explore the different identified skills to see how or why a candidate does or does not have that skill.",
|
||||
"Once you begin that action, the Start Skill Assessment button will grey out and the page will begin updating as it collates information about the candidate. As Backstory performs its magic, you can monitor the progress and explore the different identified skills to see how or why a candidate does or does not have that skill.",
|
||||
"Once it is done, you can see the final Overall Match. This is a weighted score based on amount of evidence a skill had, whether the skill was required or preferred, and other metrics."
|
||||
]}
|
||||
imageSrc={waitPng}
|
||||
|
@ -1,4 +1,5 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import CheckIcon from '@mui/icons-material/Check';
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
@ -27,7 +28,9 @@ import {
|
||||
FormControl,
|
||||
InputLabel,
|
||||
Switch,
|
||||
FormControlLabel
|
||||
FormControlLabel,
|
||||
ToggleButton,
|
||||
Checkbox
|
||||
} from '@mui/material';
|
||||
import { styled } from '@mui/material/styles';
|
||||
import {
|
||||
@ -100,6 +103,7 @@ const CandidateProfile: React.FC<BackstoryPageProps> = (props: BackstoryPageProp
|
||||
const theme = useTheme();
|
||||
const isMobile = useMediaQuery(theme.breakpoints.down('sm'));
|
||||
const { user, updateUserData, apiClient } = useAuth();
|
||||
const [isPublic, setIsPublic] = React.useState(true);
|
||||
|
||||
// Check if user is a candidate
|
||||
const candidate = user?.userType === 'candidate' ? user as Types.Candidate : null;
|
||||
@ -120,7 +124,7 @@ const CandidateProfile: React.FC<BackstoryPageProps> = (props: BackstoryPageProp
|
||||
|
||||
// Form data state
|
||||
const [formData, setFormData] = useState<Partial<Types.Candidate>>({});
|
||||
const [profileImage, setProfileImage] = useState<string | null>(null);
|
||||
const [profileImage, setProfileImage] = useState<string>('');
|
||||
|
||||
// Dialog states
|
||||
const [skillDialog, setSkillDialog] = useState(false);
|
||||
@ -146,7 +150,12 @@ const CandidateProfile: React.FC<BackstoryPageProps> = (props: BackstoryPageProp
|
||||
useEffect(() => {
|
||||
if (candidate) {
|
||||
setFormData(candidate);
|
||||
setProfileImage(candidate.profileImage || null);
|
||||
if (candidate.profileImage) {
|
||||
setProfileImage(`/api/1.0/candidates/profile/${candidate.username}`);
|
||||
} else {
|
||||
setProfileImage('');
|
||||
}
|
||||
console.log({ isPublic: candidate.isPublic });
|
||||
}
|
||||
}, [candidate]);
|
||||
|
||||
@ -177,6 +186,7 @@ const CandidateProfile: React.FC<BackstoryPageProps> = (props: BackstoryPageProp
|
||||
const handleImageUpload = async (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
if (e.target.files && e.target.files[0]) {
|
||||
if (await apiClient.uploadCandidateProfile(e.target.files[0])) {
|
||||
setProfileImage(URL.createObjectURL(e.target.files[0]));
|
||||
candidate.profileImage = 'profile.' + e.target.files[0].name.replace(/^.*\./, '');
|
||||
console.log(`Set profile image to: ${candidate.profileImage}`);
|
||||
updateUserData(candidate);
|
||||
@ -265,7 +275,7 @@ const CandidateProfile: React.FC<BackstoryPageProps> = (props: BackstoryPageProp
|
||||
<Box sx={{ textAlign: 'center', mb: { xs: 1, sm: 2 } }}>
|
||||
<Box sx={{ display: 'flex', flexDirection: 'column', alignItems: 'center' }}>
|
||||
<Avatar
|
||||
src={profileImage ? `/api/1.0/candidates/profile/${candidate.username}` : ''}
|
||||
src={profileImage}
|
||||
sx={{
|
||||
width: { xs: 80, sm: 120 },
|
||||
height: { xs: 80, sm: 120 },
|
||||
@ -298,6 +308,25 @@ const CandidateProfile: React.FC<BackstoryPageProps> = (props: BackstoryPageProp
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
<Box className="entry" sx={{ display: 'flex', justifyContent: 'center', alignContent: 'center', gap: 1, "& span": { mb: 0 } }}>
|
||||
{editMode.basic ? (
|
||||
<FormControlLabel sx={{ display: 'flex' }} control={
|
||||
<Switch
|
||||
checked={formData.isPublic}
|
||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) => handleInputChange('isPublic', event.target.checked)} />}
|
||||
label={
|
||||
formData.isPublic ?
|
||||
'Your account will appear in candidate lists and searches on Backstory.' :
|
||||
`Your account will not be listed as a candidate. You can still share your profile by users navigating to '/u/${candidate.username}'.`
|
||||
} />
|
||||
) : (<>{
|
||||
candidate.isPublic ?
|
||||
'Your account will appear in candidate lists and searches on Backstory.' :
|
||||
`Your account will not be listed as a candidate. You can still share your profile by users navigating to '/u/${candidate.username}'.`
|
||||
}</>)}
|
||||
</Box>
|
||||
|
||||
|
||||
<Box className="entry">
|
||||
{editMode.basic ? (
|
||||
<TextField
|
||||
|
@ -1,6 +1,6 @@
|
||||
// Generated TypeScript types from Pydantic models
|
||||
// Source: src/backend/models.py
|
||||
// Generated on: 2025-06-10T17:14:56.968033
|
||||
// Generated on: 2025-06-11T22:14:30.373041
|
||||
// DO NOT EDIT MANUALLY - This file is auto-generated
|
||||
|
||||
// ============================
|
||||
@ -207,6 +207,7 @@ export interface Candidate {
|
||||
jobApplications?: Array<JobApplication>;
|
||||
rags?: Array<RagEntry>;
|
||||
ragContentSize: number;
|
||||
isPublic: boolean;
|
||||
}
|
||||
|
||||
export interface CandidateAI {
|
||||
@ -241,6 +242,7 @@ export interface CandidateAI {
|
||||
jobApplications?: Array<any>;
|
||||
rags?: Array<RagEntry>;
|
||||
ragContentSize: number;
|
||||
isPublic: boolean;
|
||||
isAI: boolean;
|
||||
age?: number;
|
||||
gender?: "female" | "male";
|
||||
@ -634,6 +636,7 @@ export interface Guest {
|
||||
ipAddress?: string;
|
||||
userAgent?: string;
|
||||
ragContentSize: number;
|
||||
isPublic: boolean;
|
||||
}
|
||||
|
||||
export interface GuestCleanupRequest {
|
||||
|
@ -3121,6 +3121,7 @@ async def get_candidates(
|
||||
sortBy: Optional[str] = Query(None, alias="sortBy"),
|
||||
sortOrder: str = Query("desc", pattern="^(asc|desc)$", alias="sortOrder"),
|
||||
filters: Optional[str] = Query(None),
|
||||
current_user = Depends(get_current_user),
|
||||
database: RedisDatabase = Depends(get_database)
|
||||
):
|
||||
"""Get paginated list of candidates"""
|
||||
@ -3133,6 +3134,7 @@ async def get_candidates(
|
||||
# Get all candidates from Redis
|
||||
all_candidates_data = await database.get_all_candidates()
|
||||
candidates_list = [Candidate.model_validate(data) if not data.get("is_AI") else CandidateAI.model_validate(data) for data in all_candidates_data.values()]
|
||||
candidates_list = [c for c in candidates_list if c.is_public or c.id == current_user.id]
|
||||
|
||||
paginated_candidates, total = filter_and_paginate(
|
||||
candidates_list, page, limit, sortBy, sortOrder, filter_dict
|
||||
|
@ -604,6 +604,7 @@ class Candidate(BaseUser):
|
||||
job_applications: Optional[List["JobApplication"]] = Field(None, alias="jobApplications")
|
||||
rags: List[RagEntry] = Field(default_factory=list)
|
||||
rag_content_size : int = 0
|
||||
is_public: bool = Field(default=True, alias="isPublic")
|
||||
|
||||
class CandidateAI(Candidate):
|
||||
user_type: UserType = Field(UserType.CANDIDATE, alias="userType")
|
||||
@ -634,6 +635,7 @@ class Guest(BaseUser):
|
||||
created_at: datetime = Field(default_factory=lambda: datetime.now(UTC), alias="createdAt")
|
||||
user_agent: Optional[str] = Field(None, alias="userAgent")
|
||||
rag_content_size: int = 0
|
||||
is_public: bool = Field(default=False, alias="isPublic")
|
||||
model_config = {
|
||||
"populate_by_name": True, # Allow both field names and aliases
|
||||
"use_enum_values": True # Use enum values instead of names
|
||||
|
Loading…
x
Reference in New Issue
Block a user