Commit Graph

205 Commits (2a3c7554ba2accc5c57f8f6faaf011a4a02dd0c3)

Author SHA1 Message Date
talha.ahmed c08e28a18c Merge pull request 'Inbox: drop the Google Sheets status chip too' (#56) from Talha into main
Deploy to S3 / deploy (push) Successful in 32s Details
2026-09-02 14:13:23 +00:00
talha.ahmed 12dc77542d Merge pull request 'Inbox: drop the Microsoft Graph API status chip' (#55) from Talha into main
Deploy to S3 / deploy (push) Successful in 34s Details
2026-09-02 12:59:06 +00:00
Talha Ahmed 59afcadec9 Inbox: drop the Microsoft Graph API status chip from the header
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-02 17:57:27 +05:00
talha.ahmed 08db8dda15 Merge pull request 'Inbox: All channel first and default' (#54) from Talha into main
Deploy to S3 / deploy (push) Successful in 33s Details
2026-09-02 12:52:15 +00:00
Talha Ahmed 2bfdbdee93 Inbox: All channel first and default
The combined stream leads the channel pills (All / Email / Sheet Forms)
and is the channel the inbox opens on.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-02 17:52:03 +05:00
Talha Ahmed ec8a4af52d Inbox: All channel - Email and Sheet Forms merged in one queue
Third channel pill beside Email / Sheet Forms. Both sources are fetched
unpaged (their endpoints read a missing top/limit as no LIMIT), merged
newest-first, and paged client-side - per-source skip/top cannot compose
into a correct global page. Rows carry kind (email/form) so the detail
pane, mark-read (email rows only get checkboxes; sheet rows have no
mailbox read state) and the kind-aware state/duplicate mutations all
pick the right endpoint per row. Tabs collapse to the shared set (no
Unread), counts and totals sum both sources, and the sheet filter spans
every sheet tab on this channel.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-02 17:36:52 +05:00
talha.ahmed 5f77ba9f8e Merge pull request 'Inbox: All channel � Email and Sheet Forms merged in one queue' (#53) from Talha into main
Deploy to S3 / deploy (push) Successful in 33s Details
2026-09-02 12:36:29 +00:00
talha.ahmed ff718bccd2 Merge pull request 'Inbox: surface the All toggle at the top of the queue' (#52) from Talha into main
Deploy to S3 / deploy (push) Successful in 33s Details
2026-09-02 12:28:19 +00:00
Talha Ahmed d1fa58ecc3 Inbox: surface the All toggle at the top of the queue
The unpaged switch lived only in the bottom Per-page dropdown and went
unnoticed. The bulk bar now carries a Show all (N) / Show paged button
(hidden while rows are ticked, since the bar swaps to selection
actions); it drives the same pageSize state, so the dropdown stays in
sync. The bar wraps now - four controls no longer fit one line in the
34%-wide split column at laptop widths.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-02 17:24:23 +05:00
ahmed.mujtaba 75943cf03f Merge branch 'main' of https://git.utopiadeals.com/utopia-ai/HR-ATS-Portal into add_range 2026-09-02 17:01:48 +05:00
ahmed.mujtaba a33f4e18fc add pagination inhistory 2026-09-02 17:00:44 +05:00
Talha Ahmed 73ec556f8c Merge origin/main into Talha (history logs, page-size dropdown)
Integrates the Per-page dropdown (10/50/100) with the inbox All option:
All is now a fourth dropdown entry (opt-in via allowAll) instead of a
separate button. Restores what the stale-branch merges on main clobbered
from PR #46: mobile.test.mjs, the test:mobile script and the
puppeteer-core devDependency.
2026-09-02 16:28:25 +05:00
Talha Ahmed 3f06fbeff1 Inbox: All page-size option to show every application unpaged
PageSizeField gains an opt-in All toggle beside the numeric input
(sentinel value all; the input disables while active). Only the
Recruitment Inbox opts in: with All active the fetch omits top/limit,
which /inbox/all-applications and /sheet/form-data/fetch already read
as unpaged (Query(None) -> no LIMIT), so the whole tab renders as one
page for both the Email and Sheet Forms channels. Every other table
keeps its existing paged-only control.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-02 16:25:05 +05:00
ahmed.mujtaba 63d5232cac added range 2026-09-02 15:58:35 +05:00
ahmed.mujtaba f4c1899d5b Merge branch 'main' of https://git.utopiadeals.com/utopia-ai/HR-ATS-Portal into add_history_logs 2026-09-02 15:35:35 +05:00
ahmed.mujtaba c7d630875c add history 2026-09-02 15:33:20 +05:00
Talha Ahmed ea43b02b99 Post-merge: Progress screen mobile fixes, register Progress in smoke harness
- .progress-stage-grid gets the same min-width:0 blowout guard as .grid,
  and stacks to one column under 400px (two columns left ~56px for a
  stage label at 320)
- mobile.test.mjs: add the new progress/requisitions routes (27 total)
- __smoke__/entry.jsx: main added the /progress route but never
  registered the Progress screen in the harness map, so the smoke test
  rendered h(undefined) and /progress failed on main itself

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-02 15:32:23 +05:00
Talha Ahmed 75972d29b2 Merge origin/main into Talha (requisitions, progress screens, ATS order-by) 2026-09-02 15:19:04 +05:00
Talha Ahmed 14c78970bd Mobile responsiveness: fix grid blowouts, calendar clipping, wrapping controls; add viewport test
Audited all 25 routes at 320/375/390/430/768 with browser automation.
Root cause of most overflows: grid items default min-width:auto, so one
wide intrinsic child (a select option, the 620px RBAC matrix) pushed
whole cards past the viewport.

- .grid > * / .rbac-layout > * min-width:0 (CV Import, Interviews,
  Tasks, Access Control card blowouts)
- calendar: minmax(0,1fr) columns — SAT was clipped off-screen at 320
- .seg wraps (Tasks filters), .card-head wraps <=900
- sticky mobile table column: first column (identity), was nth-child(2)
- tooltips display:none until hover + .content overflow-x clip (killed
  a 7px sideways wiggle from invisible tooltip boxes)
- Ask Analytics form: design-system input styling, stacks on phones
- bar/grouped-bar charts thin x labels by slot width (overlap on phones)
- Candidates sort label+select wrap as one unit
- new: mobile.test.mjs + npm run test:mobile (puppeteer-core devDep,
  drives installed Chrome) — fails on sideways scroll or runtime errors

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-02 15:18:47 +05:00
ahmed.mujtaba 3ab1dc9117 progress 2026-09-02 13:47:56 +05:00
ahmed.mujtaba b0d280ec86 Merge branch 'main' of https://git.utopiadeals.com/utopia-ai/HR-ATS-Portal into Add_ATS_Order_by 2026-09-02 13:45:29 +05:00
ahmed.mujtaba 8c5549a201 Add Progress bar 2026-09-02 13:33:11 +05:00
Talha Ahmed f9fd88c5aa Dashboard redesign: org-wide activity summary with applications-per-job
Backend: new GET /analytics/applications-per-job/fetch (analytics.view) counting
inbox + manual-upload applications per job on the same basis as the funnel, with
zero-application open reqs included. Per-job group-bys added to Inbox_Messages,
Manual_UPLOAD_CANDIDATE; JobPosts.list_open_reqs for the zero-fill.

Frontend: Dashboard rewritten as a filterable summary (Week/Month/Quarter/Year +
department) - My Tasks, Today's Schedule and Quick Actions removed; 8 KPI tiles
(Hires and Offers Sent now shown); Applications per Job hero row-list; pipeline
card refed from /analytics/funnel so it obeys filters; Offer Book, Needs
Attention and Recent Activity cards added. ChartCard and time ranges extracted
to shared modules used by both Dashboard and Analytics.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-01 22:23:38 +05:00
Talha Ahmed 88b6c96a11 Talent Pool: Export button beside All Departments filter (client-side CSV of filtered grid)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-01 19:31:22 +05:00
ahmed.mujtaba 9b5c2c8053 . 2026-09-01 18:25:53 +05:00
Talha Ahmed 5a28b35dd9 Merge branch 'main' of https://git.utopiadeals.com/utopia-ai/HR-ATS-Portal into Talha
Deploy to S3 / deploy (push) Successful in 35s Details
2026-08-31 17:51:37 +05:00
ahmed.mujtaba 42e590e71d . 2026-08-31 17:35:34 +05:00
Talha Ahmed 7e76fb7874 Find Talent: Apify spend tracking + responsive fixes
- talent_runs.cost_usd records each run's actual charge (usageTotalUsd),
  accumulated across the broadened re-run ladder (migration 018).
- GET /talent/account: live balance and cycle spend from Apify
  /users/me/limits (nulls when Apify is unreachable) plus the observed
  $/profile over recorded runs.
- Header shows Balance / Spent / $-per-profile chips that refresh with
  every search; the run status line shows the last search's cost and the
  confirm dialog estimates from the live average.
- Responsive: toolbar controls stack edge-to-edge and candidate card
  footers wrap instead of clipping their buttons on phones.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-31 17:29:10 +05:00
ahmed.mujtaba 0f6de132f8 make sync visible 2026-08-31 17:03:21 +05:00
ahmed.mujtaba 8380be0d8a recieved time implemented 2026-08-31 15:51:52 +05:00
Talha Ahmed e7907a194e Inbox: responsive fixes for the split layout
- List column yields (34%, floor 280px) instead of holding 420px, so the
  detail pane stays readable in the 980-1200px band.
- Detail pane is a size container: its two-column info grid collapses on
  the pane own width, and long names/emails wrap instead of clipping.
- Tab strip wraps on narrow screens instead of running off-canvas.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-31 15:22:18 +05:00
Talha Ahmed 629554e6f6 Inbox: do not print raw To-addresses in the source chip
When no job board matches, the chip now says Email instead of the
full address; the address stays in the hover title and the opened
message. Board/referral chips are unchanged.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-31 14:39:48 +05:00
Talha Ahmed 4b93246d43 Find Talent: shortlist sourced profiles for outreach
Manual outreach funnel on talent_profiles (sourced -> shortlisted ->
contacted, one-step undo) tracked per profile with who/when stamps.
New PATCH /talent/profiles/outreach behind talent.edit; Find Talent
screen gains All/Shortlisted/Contacted tabs, a star to shortlist and
a check to record that the recruiter messaged the person on LinkedIn
(the app itself sends nothing). Re-runs never reset outreach state.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-31 14:34:23 +05:00
ahmed.mujtaba 62491d5f75 added canddiates listing 2026-08-31 13:20:08 +05:00
ahmed.mujtaba eff4c166b3 hide account coloumn 2026-08-31 13:03:29 +05:00
ahmed.mujtaba 56f529afa1 frontend skip logic done 2026-08-31 12:54:24 +05:00
ahmed.mujtaba 31742c2c49 correct sort implemented 2026-08-31 12:42:00 +05:00
ahmed.mujtaba bb056456fc the pipeline and dashboard synced 2026-08-31 00:27:27 +05:00
ahmed.mujtaba 4b84ae4a30 recruiterhub activity improvement 2026-08-30 23:24:00 +05:00
ahmed.mujtaba 9dadcb4698 dropdwon inserted 2026-08-30 22:01:43 +05:00
ahmed.mujtaba fd53730efe add hiring manager added 2026-08-30 21:49:08 +05:00
ahmed.mujtaba 7f3735362e talentpool filter workds 2026-08-30 20:50:06 +05:00
ahmed.mujtaba db176cf672 is approved and file uplaod done 2026-08-28 20:02:38 +05:00
ahmed.mujtaba e4c5ffec20 is_approved, and file_pat aded 2026-08-28 19:36:22 +05:00
ahmed.mujtaba 4e1efa559c frotnedn and bnackend crashing check 2026-08-28 18:22:13 +05:00
ahmed.mujtaba fef9fa827b candidate s3 lkinked 2026-08-28 18:10:28 +05:00
ahmed.mujtaba 617e8bae43 ditrect foirst dirtect last 2026-08-28 16:44:14 +05:00
ahmed.mujtaba b6703f31dc add cout 2026-08-28 16:38:37 +05:00
ahmed.mujtaba a21d7c3eca . 2026-08-28 16:38:04 +05:00
ahmed.mujtaba 4d343c991a . 2026-08-28 16:29:01 +05:00
ahmed.mujtaba 12fa4f87fc Merge branch 'main' of https://git.utopiadeals.com/utopia-ai/HR-ATS-Portal into RELIMIt 2026-08-28 16:28:55 +05:00
ahmed.mujtaba b5d2b05a6d Hiring manager 2026-08-28 15:54:21 +05:00
ahmed.mujtaba 7e07df18ca s3 configured pending testing 2026-08-28 13:32:25 +05:00
ahmed.mujtaba 1ab52b7292 canidate flow 2026-08-27 21:03:06 +05:00
Talha Ahmed 5575a1a6aa UI fixes and job lifecycle actions
Deploy to S3 / deploy (push) Successful in 35s Details
- Jobs list: close/reopen action per requisition with confirm on close
- Settings: real timezone and currency option lists
- Assigned-role chip: readable color pairing on soft background
- Scrollbars: Chromium honors the styled ::-webkit-scrollbar again
  (scrollbar-color now gated to Firefox), slightly wider hit area
- Topbar hamburger: hidden on desktop (specificity fix) and the drawer
  closes itself when the window grows past the mobile breakpoint
- Content scroller: removed overscroll containment that killed wheel
  scrolling everywhere except the scrollbar thumb

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-27 19:43:24 +05:00
Talha Ahmed 1ae5249d18 CV bank: production-safe file storage + candidates visibility
Deploy to S3 / deploy (push) Successful in 37s Details
The banked PDF now lives IN the database (new cv_bank_files table,
manual migration 010, auto-applied at startup) instead of the container
filesystem, so production redeploys cannot lose a stored CV; upload
writes row + bytes in one commit and creates no disk file at all. New
GET /candidate/cv-bank/file serves the bytes for both download and the
in-app preview.

Per user request, banking a CV with a detectable email also creates or
reactivates the candidate account (same pattern as manual add, no setup
email), so banked people appear on the Candidates screen; a CV without
an email still banks fine, account-less. Existing bank rows were
backfilled locally.

E2E-verified: upload -> bank row + DB bytes + zero disk files, preview
and download served from the DB, candidate visible on the Candidates
screen.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-27 19:38:40 +05:00
Talha Ahmed 0b19f3ce08 CV bank: in-app preview — view a stored CV without downloading
Deploy to S3 / deploy (push) Successful in 37s Details
Each bank row gains an eye action that opens the PDF in a modal iframe.
fetchBlobUrl learned an optional MIME re-type: /documents/download sends
octet-stream, which would trigger a download instead of the inline
viewer, so the blob is re-wrapped as application/pdf. Object URL revoked
on close. Download and remove actions unchanged.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-27 19:26:31 +05:00
ahmed.mujtaba d47e90b9ec . 2026-08-27 19:21:33 +05:00
Talha Ahmed 9f610a9078 CV bank: private storage for No-Job CV imports
Deploy to S3 / deploy (push) Successful in 36s Details
Storing a CV without a job no longer rides the cv_upload pipeline, which
created a candidate user account and an inbox row — stored CVs were
leaking into the Candidates screen. New cv-bank endpoints instead write
apply_via=cv_bank rows in manual_upload_candidate (nullable user/job
FKs): file + parsed text only, no account, no inbox entry, no scoring,
and email is optional (captured when the CV contains one). The CV Import
screen now shows the bank itself below the dropzone — browse, download
(existing /documents/download route) and delete.

E2E-verified: uploads land as BANKED rows with user_id NULL, zero new
accounts or inbox rows, UI delete works, Candidates screen unchanged.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-27 19:20:38 +05:00
Talha Ahmed d1ca1e933d CV Import: No-Job mode — store CVs in the CV bank
Deploy to S3 / deploy (push) Successful in 36s Details
The job picker gains a No job / store in CV bank option: files upload
one-per-request through the existing POST /candidate/cv_upload pipeline
(email auto-detected from the CV, saved as an UNASSIGNED inbox item with
background job suggestions) instead of being scored. Queue rows show
Stored plus the detected email; a CV with no detectable email fails
alone with a clear message. In this mode the scored grid gives way to a
panel linking Job Matching and the Inbox, where stored CVs are browsed
and later assigned. Also guarded the matcher enqueue in ingest_upload:
with the broker down the upload used to 500 after the row was inserted.

E2E-verified: two CVs stored (emails detected), rows visible in DB with
assigned_job_post_id NULL, both surfaced in Job Matching and the Inbox.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-27 19:11:13 +05:00
ahmed.mujtaba d5a51a28b0 / 2026-08-27 18:54:40 +05:00
ahmed.mujtaba b10d02f325 . 2026-08-27 17:59:21 +05:00
Talha Ahmed e6a82aaa23 Job posts: persist and display the cover image
Deploy to S3 / deploy (push) Successful in 32s Details
The create modal collected an image but dropped it. Now POST
/job/image/upload stores it on disk keyed by the post id (uuid-validated,
5 MB / png-jpg-webp-gif, replace-on-reupload; no DB migration) and GET
/job/image/fetch serves it. The create flow uploads right after the row
exists, image failure downgrades to a toast instead of failing the create,
and Job Details renders the cover via an authorized blob fetch.

E2E-verified: upload 200, fetch 200, cover renders in Job Details.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-27 16:17:55 +05:00
Talha Ahmed c22ea4c689 Job posts: remove the salary field
Deploy to S3 / deploy (push) Successful in 36s Details
Dropped from the create and edit modals, the detail drawer, the frontend
job mapper, and the composed publish text (which otherwise printed
Salary: Anonymous on every post). Backend model/API still tolerate the
column, so older clients and stored rows are unaffected.

UI-verified live: modal has no salary field, create POST carries no
salary key, cover-image upload previews and the post lands successfully.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-27 16:09:30 +05:00
Talha Ahmed 95b5de1635 Vite dev proxy: serve the SPA shell for browser navigations to API-prefixed routes
Deploy to S3 / deploy (push) Successful in 36s Details
Several API prefixes double as SPA routes (/jobs, /inbox, /pipeline, ...);
refreshing the browser on one proxied the navigation to the backend and
500d. Navigations send Accept: text/html and now bypass to /index.html,
while fetch/XHR traffic still proxies. VITE_API_TARGET repoints the proxy
when the API is not on 8000.

Verified: direct loads of /jobs, /inbox, /pipeline, /analytics, /offers
render the correct screen with the session intact; /health still proxies.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-27 11:46:28 +05:00
Talha Ahmed bc97182a7e Typography check: align inline hero numbers with the display type system
Full-app verification tour (25 screens, light + dark) confirmed Inter
Tight/Belleza/Inter render correctly; the only stragglers were inline
weight-800 numbers with px tracking in four screens, now on the same
display face, weights, and em tracking as the CSS system.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-27 11:41:00 +05:00
Talha Ahmed 9df3b46aa9 Typography overhaul: Inter Tight display headings, Belleza confined to wordmark
Belleza (single-weight decorative face) read thin and informal against
the dense data UI. Headings now use Inter Tight semibold with negative
tracking; hero metrics join the display face at proper optical sizes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-27 11:31:19 +05:00
Talha Ahmed abc8bffb81 frontend dist: bump built asset hashes
Deploy to S3 / deploy (push) Successful in 1m14s Details
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-27 10:46:21 +05:00
Talha Ahmed 5555f2475c Inbox queue rows: stop the sender name flooding the timestamp
The 380px queue rail left .ii-main only 148px: an email-style sender
name (no break points, never truncated) painted straight over the
timestamp, and the meta chips wrapped one per line. Three changes,
verified live against the dev server:

- Sender names truncate with the full value in title="" (Inbox and
  Matching, which reuses the row classes).
- Older-than-a-week list timestamps show the date only, like Outlook —
  the date+time form was ~105px and the detail pane has the full
  instant. .ii-main grows 148 -> 195px; both status chips fit one row.
- .ii-name gets min-width: 0 and its size joins the type scale.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-25 22:22:35 +05:00
Talha Ahmed 066346af80 ui-polish 7: hardening pass — skeletons, ErrorBoundary, auth font fix
- Skeleton loading system: .skeleton shimmer primitives in CSS (theme-
  aware via --border) plus a SkeletonRows component, adopted at the
  main-content loading states of Jobs, Candidates, Inbox, Interviews,
  Offers, Assessments, Tasks, Managers, Notifications. Small card-level
  loading states keep their EmptyState text.
- ErrorBoundary around the route outlet, keyed by pathname: a throw in
  one lazy screen shows a recoverable error state instead of blanking
  the whole app; navigating away resets it.
- auth.css stops hardcoding the Belleza stack twice; both sites use
  var(--font-display) so the display face has one definition.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-25 21:58:34 +05:00
Talha Ahmed 967c1acc5c ui-polish 6/6: responsive consolidation
- .g-3 grids step 3 -> 2 columns at 1200px and 2 -> 1 at 900px; they
  used to jump straight to one column, wasting the whole 900-1200 band
  (Managers cards, Help topics, JobBoard destinations).
- The four scattered trailing @media 640px fragments (Dashboard v2
  grid, candidate page, hiring forms) merged into ONE labeled block at
  the end of the file, positioned after every base rule; the paired
  400px override moves with it so small-phone rules still win. The
  core shell 640 block stays put so the landscape-phone height rules
  keep their existing precedence.
- Every breakpoint labeled; responsive section header updated.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-25 21:55:00 +05:00
Talha Ahmed c4793479a2 ui-polish 5/6: a11y semantics and remaining labels
- All 28 <div className="form-section-title"> section markers are real
  <h3> headings now — the class carries every visual property, so this
  is semantics-only, zero visual change.
- Remaining icon-only .act-btn buttons labeled: CandidateProfile
  (download/edit note/revise scorecard, incl. one that had neither
  tooltip nor label), CandidateForms amend button.
- PasswordField show/hide toggle is keyboard-reachable (tabIndex -1
  removed) with aria-pressed state.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-25 21:53:08 +05:00
Talha Ahmed af6ef715c5 ui-polish 4/6: screen sweep B (17 remaining page screens)
AiAssistant, AiStudio, Analytics, Assessments, Calendar, CvImport, Help,
JobBoard, Notifications, Offers, Pipeline, Rbac, RecruiterHub, Reports,
Talent, TalentPool, Tasks — same bounded checklist:

- Every remaining hand-rolled .page-head replaced with <PageHeader/>;
  zero copies of the block remain in the codebase.
- JobBoard fixes its non-functional inline ellipsis (overflow without
  white-space never truncated) with .truncate + title.
- Tasks imports the shared PRIORITY_CLASS and labels its unlabeled
  card-head add button; remaining .act-btn tooltips mirrored into
  aria-labels (Assessments, JobBoard, Notifications, Offers, Talent).
- Assessments swaps a hand-rolled empty state for the imported
  EmptyState; Help drops an inline h2 font size the base rule now covers.
- Mechanical inline-style swaps to utilities on exact matches.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-25 21:51:26 +05:00
Talha Ahmed 4757895029 ui-polish 3/6: screen sweep A (9 representative screens)
Dashboard, Candidates, Jobs, Settings, Inbox, Interviews, Matching,
Managers, JobCandidates — bounded checklist per screen:

- Hand-rolled .page-head blocks replaced with <PageHeader/>. Fixes
  Matching, whose bare <h1> rendered in the wrong face and weight.
- Candidates drops its verbatim copy of the DataTable thead for
  DataTableHead; clickable rows use .row-click.
- Interviews deletes its local STATUS_CLASS shadow (Badge auto-maps
  identically); Dashboard imports the shared PRIORITY_CLASS.
- Icon-only .act-btn buttons gain aria-labels mirroring their tooltips.
- Emails no longer overflow: Settings/Candidates clip with full text in
  title=""; Managers shows the full address again instead of chopping
  the domain off the data.
- Mechanical inline-style swaps to the new utilities (mb-*, mt-*,
  flex-1, justify-between, flex-wrap) on exact matches only.
- Settings uses its imported EmptyState instead of a hand-rolled copy;
  JobCandidates embedded title uses .page-title-sm instead of an inline
  font size.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-25 21:46:11 +05:00
Talha Ahmed bd16f8a775 ui-polish 2/6: PageHeader, DataTable dedup, Tabs/Toast/Dropdown a11y
- New ui/PageHeader.jsx emitting the existing .page-head structure;
  screens adopt it in the next two passes.
- DataTable: sortable thead extracted as DataTableHead (Candidates can
  drop its verbatim copy) and an onRowClick prop with keyboard support.
- Tabs: index-based ids, aria-controls, roving tabindex, arrow-key and
  Home/End navigation; TabPanel labels its panel.
- Toast root announces politely to screen readers (was silent).
- Dropdown stamps aria-expanded/aria-haspopup on every trigger.
- primitives: shared PRIORITY_CLASS (Dashboard and Tasks had identical
  private copies).
- Shell: skip link to #main-content, sidebar nav labeled Primary,
  clickable notification rows are real buttons now.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-25 21:39:31 +05:00
Talha Ahmed bc5856005d ui-polish 1/6: typography+spacing tokens, base headings, utilities
- Type scale (--fs-2xs..--fs-3xl) and line-height tokens; the 20-size
  census collapses onto 9 steps. Spacing scale (--space-1..8) plus --gap
  keeping the signature 18px rhythm. Colors untouched.
- Base h1-h6 rules: bare headings used to render at UA defaults, larger
  and bolder than the styled card titles beside them.
- One eyebrow-label rule replacing eleven near-identical declarations at
  five sizes and five letter-spacings.
- High-traffic families retrofitted to tokens (page/card/table/form/btn/
  tabs/badge/sidebar/kpi); rare bolt-on sections left alone.
- New utilities: truncate, clamp-2, justify-between, flex-wrap, flex-col,
  flex-1, min-w-0, w-full, mt/mb-8/12/16/24, cell-clip, row-click.
- Defined phantom classes shipped in JSX (.dt, .route-loading,
  .tab-count) and a .skip-link.
- Email/role cells clip with ellipsis on all viewports, not only <640px.
- Content measure capped at 1440px on every desktop, not only >=1600.
- Sidebar: clearer group separation, active item weight 600.
- main.jsx design-contract comment amended: colors stay frozen,
  typography/spacing re-tokenized.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-25 21:37:14 +05:00
Talha Ahmed 4ad964e1ff Talent filter uses the standard toolbar-search box
className="input" matches no CSS rule, so the filter fell back to the
browser default ~180px width and clipped its own placeholder ("Filter
by name, headline, com"). Swap it for the toolbar-search pattern every
other screen uses: search icon, full-width input, 340px cap.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-25 19:49:08 +05:00
ahmed.mujtaba d9992e46d1 correct the error 2026-08-25 12:54:04 +05:00
ahmed.mujtaba 347856e8f8 Update environment management by removing `.env.example` and refining Docker configurations
- Deleted the `.env.example` file to centralize configurations in `backend/.env`.
- Adjusted `docker-compose.yml` and `docker-compose.dev.yml` for improved environment variable handling.
- Enhanced nginx configuration for better API request management.

These changes streamline the setup process and improve configuration management.
2026-08-25 12:31:42 +05:00
Talha Ahmed 9ffb3caa99 Merge remote-tracking branch 'origin/main' into Talha 2026-08-24 22:22:11 +05:00
Talha Ahmed 8bfd71194e Forms also unlock when the candidate has an interview scheduled
Scheduling an interview record did not move the pipeline stage, so
recruiters who booked an interview from the profile still saw the Forms
tab locked (reported on a Shortlist-stage candidate with a completed
Technical interview). The gate now passes when the stage is
INTERVIEW/OFFER/HIRED OR the application has any interview on record —
enforced server-side in candidate_forms create (manual-upload candidates
still unlock by stage alone, since interviews link only to inbox rows)
and mirrored in the Forms tab hint.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-24 22:21:36 +05:00
ahmed.mujtaba 29c0362517 Merge branch 'main' of https://git.utopiadeals.com/utopia-ai/HR-ATS-Portal into Implementing_limitListing 2026-08-24 20:39:31 +05:00
ahmed.mujtaba 8c12872c6e Remove `.env.example` and update Docker configurations for environment management
- Deleted the `.env.example` file as it is no longer needed; all configurations are now centralized in `backend/.env`.
- Updated `docker-compose.yml` and `docker-compose.dev.yml` to reflect changes in environment variable handling, ensuring that the application reads from `backend/.env` exclusively.
- Adjusted the nginx configuration to improve API request handling and ensure proper proxying for frontend interactions.

These changes streamline the environment setup process and enhance the overall configuration management for local and production deployments.
2026-08-24 20:38:15 +05:00
Talha Ahmed f9eb9f1f24 Candidate hiring forms (Annexure A/E/J), full-page profile, UX audit fixes
Deploy to S3 / deploy (push) Successful in 37s Details
Backend: new candidate_forms domain (requisition, interview analysis,
cultural fit) with XOR inbox/manual keys, server-recomputed section
averages and combined summary, INTERVIEW-stage gate (409), history
events, INTERVIEWS_* permissions + 008 RBAC seed; offers table gains
the seven Annexure-J fields.

Frontend: Forms tab in the candidate profile (paper-exact labels from
/forms/definitions, rating tables, score summary tiles, completion
dots); profile converted to a full page at /candidate/:userId opened
from Candidates, Talent Pool and Pipeline; live Advance Stage now calls
PATCH /candidate/stage; workflow-ordered tabs; responsive pass verified
by headless-Edge screenshots at 375-2400px; Stars import crash fix in
Interviews; Matching tab strip wraps on phones.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-24 20:14:36 +05:00
Talha Ahmed 9e39bc5762 Dashboard audit fixes, Find Talent rename, already-applied matching
Dashboard/chart audit (senior-analyst pass over the live app):
- Pipeline bars were all 0% while the doughnut showed candidates: the
  percentage base was the first stage count (PROCESS, empty). Bars are
  now each stage share of the active total, REJECTED is excluded
  (matching the Analytics card), and stages sort in pipeline order.
- KPI trend chips no longer show a green up-arrow beside an empty delta;
  for lower-is-better metrics (time to hire, cost per hire) the colour
  tracks goodness while the arrow tracks the data direction.
- Closed Jobs tile no longer adds hires to closed requisitions and no
  longer hardcodes an up arrow; Offers Accepted drops the hires-series
  sparkline that plotted the wrong metric.
- charts.js: integer-friendly Y ticks (no more 0,1,1,2,2), edge-hugging
  X labels (last month was clipped), and label thinning on packed axes.

Find Talent: nav item and page title renamed from Talent to match the
action button and to distinguish it from Talent Pool.

Already-applied matching (Find Talent x ATS):
- linkedin_utils.py: shared /in/<slug> extraction that survives PDF
  mangling (percent-escapes, no scheme, trailing punctuation, /pub/).
- inbox_messages.linkedin_slug + manual_upload_candidate.linkedin_slug
  (indexed; empty = scanned-none, NULL = unscanned) written as CVs are
  processed and lazily backfilled in bounded batches at fetch time.
- talent/matching.py annotates profile payloads with already_applied
  (source, status, job, same_job, applications); profile cards and the
  detail modal show a green Already-applied / amber In-ATS-other-job
  chip.

Verified live: seeded CV mention matched its sourced profile with zero
false positives across 50 real Apify profiles; 57 backend tests and the
frontend build + smoke suites pass.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-21 20:12:16 +05:00
Talha Ahmed 600d438af3 Analytics: report library, NL ask, TTH baseline, source spend
Builds the four remaining analytics gaps from the architecture plan:

- Report library (REQ-ANL-03): new backend/reports module - saved reports
  as parameterisations of the governed analytics queries (never free-form
  SQL), rolling window_days filters, recorded runs, CSV export via
  Content-Disposition. Reports screen gains the library card with
  create/run/export/delete plus a results modal.
- Natural-language analytics (REQ-ANL-05, ADR-0010): POST /analytics/ask
  maps a question onto one whitelisted intent, runs the same governed
  query the dashboard uses, then narrates the numbers. Ask Analytics card
  on the Analytics screen; LLM outages 503 without touching the charts.
- Time-to-hire baseline (REQ-ANL-08): KPIs surface tth_baseline_* from
  org setting analytics.tth_baseline ({days,source}); not hardcoded
  per OPEN-12. analytics added to org-settings categories. Reports TTH
  card shows the delta when set.
- Source spend (REQ-ANL-09 cost side): hiring costs can be tagged with a
  source channel; source performance returns tagged spend and
  cost-per-application, including spend-only rows for channels with zero
  applications (wasted spend must stay visible). Log-cost modal and
  Source Performance table on Reports; untagged spend stays
  cost-per-hire only.

Verified live: migration autogenerated and applied (saved_reports,
report_runs, hiring_costs.source_channel_id), 50 backend tests, vite
build + smoke/token/theme suites, and a headless-browser drive of both
screens end to end.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-21 17:24:35 +05:00
ahmed.mujtaba a8b213be39 commited 2026-08-21 13:00:22 +05:00
Talha Ahmed 5330a2504e Merge branch 'Talha'
Deploy to S3 / deploy (push) Successful in 41s Details
# Conflicts:
#	.gitignore
#	backend/main.py
2026-08-20 22:29:09 +05:00
Talha Ahmed e1bd8526f3 Talent tab: LinkedIn sourcing per job via Apify
New backend/talent/ module (runs + profiles tables, RBAC talent.* seed,
frontend-polled run lifecycle) and frontend Talent screen: job + location
pickers, paid-search confirm, big loader, ranked profile cards with match
ring, profile detail modal, show-10-then-more, next-page re-search.

Sourcing quality: current-title facet + skills-only keyword query,
experience-range facet, thin-result broadening ladder, Utopia Brands/Deals
current employees excluded actor-side and server-side, graded 0-100
relevance score (title phrase over keyword stuffing).

Also narrows the overbroad **_**_**.py gitignore rule to alembic versions;
it was silently swallowing app/tests __init__.py files, the engine smoke
script, and the new talent plugin tests.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-20 22:27:18 +05:00
ahmed.mujtaba c536def585 Update Docker and API configurations for improved job fetching and API proxying
- Adjusted `VITE_API_BASE` in Dockerfile and docker-compose.yml to allow same-origin requests, enhancing compatibility with nginx proxy settings.
- Increased the `top` query limit in `app.py` to 500 to accommodate frontend requirements while ensuring consistency across job fetching in `Jobs.jsx` and `Managers.jsx`.
- Updated nginx configuration to properly proxy API requests, preventing incorrect responses for job-related endpoints.

These changes streamline the interaction between the frontend and backend, ensuring a smoother user experience when fetching job data.
2026-08-20 18:55:49 +05:00
ahmed.mujtaba 40edc092a3 top limit maitnained 2026-08-20 18:40:24 +05:00
ahmed.mujtaba b79009679c Merge branch 'main' of https://git.utopiadeals.com/utopia-ai/HR-ATS-Portal into Implementing_limitListing 2026-08-20 18:22:40 +05:00
ahmed.mujtaba 867fea495a m. 2026-08-20 18:22:33 +05:00
ahmed.mujtaba f1758ac9bd Add mailbox sync functionality and related API endpoints
- Introduced a new `MailboxSyncRun` model to manage mailbox synchronization jobs, allowing for tracking of sync status and results.
- Implemented API endpoints for starting mailbox sync and fetching sync status, enabling asynchronous processing of email synchronization.
- Updated the `Email` service to handle mailbox sync operations, including enqueueing tasks and managing sync results.
- Enhanced frontend components to initiate mailbox sync and display sync status, improving user experience and feedback during the sync process.
- Added necessary query keys and local storage management for tracking sync runs in the UI.

This commit enhances the application's email synchronization capabilities, providing a more robust and user-friendly experience for managing mailbox data.
2026-08-20 17:55:17 +05:00
ahmed.mujtaba c7fc2fed10 Implement candidate history tracking and enhance email URL configuration
- Added a new `CandidateHistory` model to maintain an append-only audit log for candidate actions.
- Introduced `HistoryRecorder` functionality to log significant events across various modules, including interviews, feedback, and notes.
- Updated existing methods to include `current_user` for tracking who performed actions.
- Enhanced the `EMAIL_URL` configuration in `docker-compose.yml` to allow for environment variable overrides.
- Updated frontend components to support fetching and displaying candidate history.

This commit improves the application's ability to track changes and actions related to candidates, enhancing accountability and transparency.
2026-08-20 17:22:22 +05:00
ahmed.mujtaba cd35af8161 Enhance interview and job functionalities
- Updated the interview model to include `graph_event_id` and `web_link` for calendar integration.
- Added methods for setting calendar events and retrieving job titles associated with interviews.
- Modified serializers to include new fields in the interview response.
- Enhanced the job post model to include applicant counts.
- Updated frontend components to handle new fields and improve user experience with calendar event management.
- Introduced image upload functionality in job forms with validation for file types and sizes.
- Improved dashboard and calendar UI for better data presentation.

This commit streamlines interview scheduling and job management processes, enhancing overall application functionality.
2026-08-20 16:41:21 +05:00
Talha Ahmed 202d05c4f9 Real Excel export for the Jobs list
- GET /jobs/export (jobs.export): styled .xlsx via openpyxl — branded
  banner, dark-green frozen header with auto-filter, zebra rows, color-
  coded status, bulleted requirements/nice-to-have, date formatting.
  Honors the same filters as /jobs/fetch.
- Export button on the Jobs screen now downloads the file through the
  authenticated downloadFile helper, carrying the active UI filters,
  with an Exporting... busy state (was a fake success toast).
- openpyxl pinned in backend requirements.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-19 20:10:42 +05:00
Talha Ahmed 9eb1a5f311 Rename assist preview button from Redo to Regenerate
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-19 20:02:13 +05:00
Talha Ahmed 878d1e3776 Add AI sparkles icon to the Create Job button
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-19 19:56:33 +05:00
ahmed.mujtaba 1c1a774dc8 Merge branch 'main' of https://git.utopiadeals.com/utopia-ai/HR-ATS-Portal into UI_CHANGES 2026-08-19 19:34:57 +05:00