Dashboard_Wiring
ahmed.mujtaba 2026-08-12 13:57:53 +05:00
parent 4d84e5c5c9
commit 038d89ced1
5346 changed files with 1320254 additions and 0 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

View File

@ -0,0 +1,53 @@
"""auto
Revision ID: 2515ed7e2966
Revises: 72853b8d2126
Create Date: 2026-08-07 14:49:26.409410+00:00
"""
from __future__ import annotations
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
import sqlmodel # SQLModel renders AutoString() into migrations but adds no import
revision: str = '2515ed7e2966'
down_revision: Union[str, None] = '72853b8d2126'
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
# Deliberately NOT schema-qualified: Inbox_Messages.application_status carries
# type.schema = None, so the ORM emits the type name unqualified and resolves it
# through search_path. Creating it as app.<name> would not match.
application_status = sa.Enum(
'PROCESS', 'PENDING', 'APPROVED', 'REJECTED', 'ONHOLD', 'CLOSED',
name='candidate_application_status',
)
def upgrade() -> None:
# op.add_column does NOT emit CREATE TYPE, so the ALTER below would fail with
# `type "candidate_application_status" does not exist`.
application_status.create(op.get_bind(), checkfirst=True)
# server_default is required, not cosmetic: the column is NOT NULL and every
# existing row would be null without it, which Postgres rejects outright.
op.add_column(
'inbox_messages',
sa.Column(
'application_status',
application_status,
nullable=False,
server_default='CLOSED',
),
schema='app',
)
def downgrade() -> None:
op.drop_column('inbox_messages', 'application_status', schema='app')
# drop_column leaves the TYPE behind; without this, re-running upgrade fails
# with `type "candidate_application_status" already exists`.
application_status.drop(op.get_bind(), checkfirst=True)

View File

@ -0,0 +1,40 @@
"""auto
Revision ID: 8c67df87f19c
Revises: 2515ed7e2966
Create Date: 2026-08-07 14:55:42.858317+00:00
"""
from __future__ import annotations
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
import sqlmodel # SQLModel renders AutoString() into migrations but adds no import
from sqlalchemy.dialects import postgresql
revision: str = '8c67df87f19c'
down_revision: Union[str, None] = '2515ed7e2966'
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.alter_column('inbox_messages', 'application_status',
existing_type=postgresql.ENUM('PROCESS', 'PENDING', 'APPROVED', 'REJECTED', 'ONHOLD', 'CLOSED', name='candidate_application_status'),
server_default=None,
existing_nullable=False,
schema='app')
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.alter_column('inbox_messages', 'application_status',
existing_type=postgresql.ENUM('PROCESS', 'PENDING', 'APPROVED', 'REJECTED', 'ONHOLD', 'CLOSED', name='candidate_application_status'),
server_default=sa.text("'CLOSED'::candidate_application_status"),
existing_nullable=False,
schema='app')
# ### end Alembic commands ###

View File

@ -0,0 +1,32 @@
"""add experience to inbox_messages
Revision ID: fcd3f4d69b60
Revises: 8c67df87f19c
Create Date: 2026-08-10 09:56:55.290497+00:00
"""
from __future__ import annotations
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
import sqlmodel # SQLModel renders AutoString() into migrations but adds no import
revision: str = 'fcd3f4d69b60'
down_revision: Union[str, None] = '8c67df87f19c'
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('inbox_messages', sa.Column('experience', sqlmodel.sql.sqltypes.AutoString(), nullable=True), schema='app')
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column('inbox_messages', 'experience', schema='app')
# ### end Alembic commands ###

View File

@ -0,0 +1,34 @@
"""auto
Revision ID: ed78f1706042
Revises: fcd3f4d69b60
Create Date: 2026-08-11 10:07:53.324376+00:00
"""
from __future__ import annotations
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
import sqlmodel # SQLModel renders AutoString() into migrations but adds no import
revision: str = 'ed78f1706042'
down_revision: Union[str, None] = 'fcd3f4d69b60'
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('inbox_messages', sa.Column('candidate_phone_number', sqlmodel.sql.sqltypes.AutoString(), nullable=True), schema='app')
op.add_column('inbox_messages', sa.Column('current_employment', sqlmodel.sql.sqltypes.AutoString(), nullable=True), schema='app')
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column('inbox_messages', 'current_employment', schema='app')
op.drop_column('inbox_messages', 'candidate_phone_number', schema='app')
# ### end Alembic commands ###

View File

@ -0,0 +1,90 @@
"""auto
Revision ID: d0b62342a32f
Revises: ed78f1706042
Create Date: 2026-08-11 11:49:25.965217+00:00
"""
from __future__ import annotations
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
import sqlmodel # SQLModel renders AutoString() into migrations but adds no import
revision: str = 'd0b62342a32f'
down_revision: Union[str, None] = 'ed78f1706042'
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.create_table('notes',
sa.Column('id', sa.Uuid(), nullable=False),
sa.Column('note', sqlmodel.sql.sqltypes.AutoString(), nullable=False),
sa.Column('created_at', sa.DateTime(), nullable=False),
sa.Column('updated_at', sa.DateTime(), nullable=False),
sa.Column('user_id', sa.Uuid(), nullable=True),
sa.Column('created_by', sa.Uuid(), nullable=True),
sa.ForeignKeyConstraint(['created_by'], ['app.users.id'], name=op.f('fk_notes_created_by_users')),
sa.ForeignKeyConstraint(['user_id'], ['app.users.id'], name=op.f('fk_notes_user_id_users')),
sa.PrimaryKeyConstraint('id', name=op.f('pk_notes')),
schema='app'
)
op.create_table('activity',
sa.Column('id', sa.Uuid(), nullable=False),
sa.Column('activity_type', sqlmodel.sql.sqltypes.AutoString(), nullable=False),
sa.Column('activity_date', sa.DateTime(), nullable=False),
sa.Column('activity_time', sa.DateTime(), nullable=False),
sa.Column('activity_status', sqlmodel.sql.sqltypes.AutoString(), nullable=False),
sa.Column('description', sqlmodel.sql.sqltypes.AutoString(), nullable=True),
sa.Column('inbox_id', sa.Integer(), nullable=True),
sa.ForeignKeyConstraint(['inbox_id'], ['app.inbox.id'], name=op.f('fk_activity_inbox_id_inbox')),
sa.PrimaryKeyConstraint('id', name=op.f('pk_activity')),
schema='app'
)
op.create_table('feedback',
sa.Column('id', sa.Uuid(), nullable=False),
sa.Column('review', sqlmodel.sql.sqltypes.AutoString(), nullable=False),
sa.Column('financial_status', sqlmodel.sql.sqltypes.AutoString(), nullable=False),
sa.Column('score', sa.Float(), nullable=False),
sa.Column('note', sqlmodel.sql.sqltypes.AutoString(), nullable=True),
sa.Column('created_at', sa.DateTime(), nullable=False),
sa.Column('updated_at', sa.DateTime(), nullable=False),
sa.Column('reviewed_by', sa.Uuid(), nullable=True),
sa.Column('inbox_id', sa.Integer(), nullable=True),
sa.ForeignKeyConstraint(['inbox_id'], ['app.inbox.id'], name=op.f('fk_feedback_inbox_id_inbox')),
sa.ForeignKeyConstraint(['reviewed_by'], ['app.users.id'], name=op.f('fk_feedback_reviewed_by_users')),
sa.PrimaryKeyConstraint('id', name=op.f('pk_feedback')),
schema='app'
)
op.create_table('interviews',
sa.Column('id', sa.Uuid(), nullable=False),
sa.Column('interview_date', sa.DateTime(), nullable=False),
sa.Column('interview_time', sa.DateTime(), nullable=False),
sa.Column('interview_type', sqlmodel.sql.sqltypes.AutoString(), nullable=False),
sa.Column('interview_status', sqlmodel.sql.sqltypes.AutoString(), nullable=False),
sa.Column('inbox_id', sa.Integer(), nullable=True),
sa.ForeignKeyConstraint(['inbox_id'], ['app.inbox.id'], name=op.f('fk_interviews_inbox_id_inbox')),
sa.PrimaryKeyConstraint('id', name=op.f('pk_interviews')),
schema='app'
)
op.add_column('inbox', sa.Column('favorite', sa.Boolean(), nullable=True), schema='app')
op.add_column('inbox', sa.Column('rating', sa.Float(), nullable=True), schema='app')
op.add_column('inbox_messages', sa.Column('candidate_education', sqlmodel.sql.sqltypes.AutoString(), nullable=True), schema='app')
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column('inbox_messages', 'candidate_education', schema='app')
op.drop_column('inbox', 'rating', schema='app')
op.drop_column('inbox', 'favorite', schema='app')
op.drop_table('interviews', schema='app')
op.drop_table('feedback', schema='app')
op.drop_table('activity', schema='app')
op.drop_table('notes', schema='app')
# ### end Alembic commands ###

View File

@ -0,0 +1,110 @@
"""auto
Revision ID: 681a78158447
Revises: d0b62342a32f
Create Date: 2026-08-11 13:11:58.032282+00:00
"""
from __future__ import annotations
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
import sqlmodel # SQLModel renders AutoString() into migrations but adds no import
from sqlalchemy.dialects import postgresql
revision: str = '681a78158447'
down_revision: Union[str, None] = 'd0b62342a32f'
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.alter_column('activity', 'activity_date',
existing_type=postgresql.TIMESTAMP(),
type_=sa.DateTime(timezone=True),
existing_nullable=False,
schema='app')
op.alter_column('activity', 'activity_time',
existing_type=postgresql.TIMESTAMP(),
type_=sa.DateTime(timezone=True),
existing_nullable=False,
schema='app')
op.alter_column('feedback', 'created_at',
existing_type=postgresql.TIMESTAMP(),
type_=sa.DateTime(timezone=True),
existing_nullable=False,
schema='app')
op.alter_column('feedback', 'updated_at',
existing_type=postgresql.TIMESTAMP(),
type_=sa.DateTime(timezone=True),
existing_nullable=False,
schema='app')
op.alter_column('interviews', 'interview_date',
existing_type=postgresql.TIMESTAMP(),
type_=sa.DateTime(timezone=True),
existing_nullable=False,
schema='app')
op.alter_column('interviews', 'interview_time',
existing_type=postgresql.TIMESTAMP(),
type_=sa.DateTime(timezone=True),
existing_nullable=False,
schema='app')
op.alter_column('notes', 'created_at',
existing_type=postgresql.TIMESTAMP(),
type_=sa.DateTime(timezone=True),
existing_nullable=False,
schema='app')
op.alter_column('notes', 'updated_at',
existing_type=postgresql.TIMESTAMP(),
type_=sa.DateTime(timezone=True),
existing_nullable=False,
schema='app')
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.alter_column('notes', 'updated_at',
existing_type=sa.DateTime(timezone=True),
type_=postgresql.TIMESTAMP(),
existing_nullable=False,
schema='app')
op.alter_column('notes', 'created_at',
existing_type=sa.DateTime(timezone=True),
type_=postgresql.TIMESTAMP(),
existing_nullable=False,
schema='app')
op.alter_column('interviews', 'interview_time',
existing_type=sa.DateTime(timezone=True),
type_=postgresql.TIMESTAMP(),
existing_nullable=False,
schema='app')
op.alter_column('interviews', 'interview_date',
existing_type=sa.DateTime(timezone=True),
type_=postgresql.TIMESTAMP(),
existing_nullable=False,
schema='app')
op.alter_column('feedback', 'updated_at',
existing_type=sa.DateTime(timezone=True),
type_=postgresql.TIMESTAMP(),
existing_nullable=False,
schema='app')
op.alter_column('feedback', 'created_at',
existing_type=sa.DateTime(timezone=True),
type_=postgresql.TIMESTAMP(),
existing_nullable=False,
schema='app')
op.alter_column('activity', 'activity_time',
existing_type=sa.DateTime(timezone=True),
type_=postgresql.TIMESTAMP(),
existing_nullable=False,
schema='app')
op.alter_column('activity', 'activity_date',
existing_type=sa.DateTime(timezone=True),
type_=postgresql.TIMESTAMP(),
existing_nullable=False,
schema='app')
# ### end Alembic commands ###

View File

@ -0,0 +1,36 @@
"""auto
Revision ID: 67c4c7974afa
Revises: 681a78158447
Create Date: 2026-08-11 13:52:11.674108+00:00
"""
from __future__ import annotations
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
import sqlmodel # SQLModel renders AutoString() into migrations but adds no import
revision: str = '67c4c7974afa'
down_revision: Union[str, None] = '681a78158447'
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('inbox_messages', sa.Column('assigned_job_post_id', sa.Uuid(), nullable=True), schema='app')
op.create_index(op.f('ix_inbox_messages_assigned_job_post_id'), 'inbox_messages', ['assigned_job_post_id'], unique=False, schema='app')
op.create_foreign_key(op.f('fk_inbox_messages_assigned_job_post_id_job_posts'), 'inbox_messages', 'job_posts', ['assigned_job_post_id'], ['id'], source_schema='app', referent_schema='app')
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.drop_constraint(op.f('fk_inbox_messages_assigned_job_post_id_job_posts'), 'inbox_messages', schema='app', type_='foreignkey')
op.drop_index(op.f('ix_inbox_messages_assigned_job_post_id'), table_name='inbox_messages', schema='app')
op.drop_column('inbox_messages', 'assigned_job_post_id', schema='app')
# ### end Alembic commands ###

View File

@ -0,0 +1,52 @@
"""auto
Revision ID: 4b34909ae551
Revises: 67c4c7974afa
Create Date: 2026-08-12 07:46:38.028948+00:00
"""
from __future__ import annotations
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
import sqlmodel # SQLModel renders AutoString() into migrations but adds no import
revision: str = '4b34909ae551'
down_revision: Union[str, None] = '67c4c7974afa'
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.create_table('manual_upload_candidate',
sa.Column('id', sa.Uuid(), nullable=False),
sa.Column('candidate_email', sqlmodel.sql.sqltypes.AutoString(), nullable=False),
sa.Column('candidate_name', sqlmodel.sql.sqltypes.AutoString(), nullable=False),
sa.Column('candidate_phone', sqlmodel.sql.sqltypes.AutoString(), nullable=False),
sa.Column('job_post_id', sa.Uuid(), nullable=True),
sa.Column('full_text', sqlmodel.sql.sqltypes.AutoString(), nullable=False),
sa.Column('current_company', sqlmodel.sql.sqltypes.AutoString(), nullable=False),
sa.Column('user_id', sa.Uuid(), nullable=True),
sa.Column('platform', sqlmodel.sql.sqltypes.AutoString(), nullable=False),
sa.Column('created_by', sa.Uuid(), nullable=True),
sa.Column('experience', sqlmodel.sql.sqltypes.AutoString(), nullable=False),
sa.Column('status', sqlmodel.sql.sqltypes.AutoString(), nullable=False),
sa.Column('created_at', sa.DateTime(timezone=True), nullable=False),
sa.Column('updated_at', sa.DateTime(timezone=True), nullable=False),
sa.ForeignKeyConstraint(['created_by'], ['app.users.id'], name=op.f('fk_manual_upload_candidate_created_by_users')),
sa.ForeignKeyConstraint(['job_post_id'], ['app.job_posts.id'], name=op.f('fk_manual_upload_candidate_job_post_id_job_posts')),
sa.ForeignKeyConstraint(['user_id'], ['app.users.id'], name=op.f('fk_manual_upload_candidate_user_id_users')),
sa.PrimaryKeyConstraint('id', name=op.f('pk_manual_upload_candidate')),
schema='app'
)
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.drop_table('manual_upload_candidate', schema='app')
# ### end Alembic commands ###

View File

@ -0,0 +1,32 @@
"""auto
Revision ID: 1a7c0e30aaab
Revises: 4b34909ae551
Create Date: 2026-08-12 08:17:21.212665+00:00
"""
from __future__ import annotations
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
import sqlmodel # SQLModel renders AutoString() into migrations but adds no import
revision: str = '1a7c0e30aaab'
down_revision: Union[str, None] = '4b34909ae551'
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('manual_upload_candidate', sa.Column('referral_by', sqlmodel.sql.sqltypes.AutoString(), server_default='', nullable=False), schema='app')
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column('manual_upload_candidate', 'referral_by', schema='app')
# ### end Alembic commands ###

View File

@ -0,0 +1,34 @@
"""auto
Revision ID: 6ec5e105dce3
Revises: 1a7c0e30aaab
Create Date: 2026-08-12 08:45:01.957310+00:00
"""
from __future__ import annotations
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
import sqlmodel # SQLModel renders AutoString() into migrations but adds no import
revision: str = '6ec5e105dce3'
down_revision: Union[str, None] = '1a7c0e30aaab'
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('manual_upload_candidate', sa.Column('file_name', sqlmodel.sql.sqltypes.AutoString(), server_default='', nullable=False), schema='app')
op.add_column('manual_upload_candidate', sa.Column('file_path', sqlmodel.sql.sqltypes.AutoString(), server_default='', nullable=False), schema='app')
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column('manual_upload_candidate', 'file_path', schema='app')
op.drop_column('manual_upload_candidate', 'file_name', schema='app')
# ### end Alembic commands ###

View File

@ -0,0 +1,58 @@
"""Taskiq CV-upload broker — isolated Redis stream so manual uploads
never sit behind the bulk /email/fetch backlog.
Worker: taskiq worker taskiq_management.cv_broker_setup:cv_broker inbox.cv_tasks
Scheduler: taskiq scheduler taskiq_management.cv_broker_setup:cv_scheduler inbox.cv_tasks
"""
from __future__ import annotations
import os
from dotenv import load_dotenv
from taskiq import TaskiqScheduler
from taskiq.middlewares import SmartRetryMiddleware
from taskiq.schedule_sources import LabelScheduleSource
from taskiq_redis import (
ListRedisScheduleSource,
RedisAsyncResultBackend,
RedisStreamBroker,
)
from taskiq_management.broker_setup import MAX_RETRIES,RETRY_DELAY
from taskiq_management.middleware import DeadLetterMiddleware
load_dotenv()
REDIS_URL=os.getenv("REDIS_URL","redis://localhost:6379/0")
CV_QUEUE_NAME=os.getenv("TASKIQ_CV_QUEUE_NAME","cv_upload")
result_backend=RedisAsyncResultBackend(redis_url=REDIS_URL)
cv_schedule_source=ListRedisScheduleSource(url=REDIS_URL,prefix="taskiq:schedule:cv")
cv_broker=(
RedisStreamBroker(
url=REDIS_URL,
queue_name=CV_QUEUE_NAME,
consumer_group_name=os.getenv("TASKIQ_CONSUMER_GROUP","taskiq"),
idle_timeout=int(os.getenv("TASKIQ_IDLE_TIMEOUT_MS","600000")),
)
.with_result_backend(result_backend)
.with_middlewares(
DeadLetterMiddleware(redis_url=REDIS_URL),
SmartRetryMiddleware(
default_retry_count=MAX_RETRIES,
default_retry_label=True,
default_delay=RETRY_DELAY,
use_jitter=True,
use_delay_exponent=True,
max_delay_exponent=float(os.getenv("TASKIQ_MAX_DELAY","120")),
schedule_source=cv_schedule_source,
),
)
)
cv_scheduler=TaskiqScheduler(
broker=cv_broker,
sources=[cv_schedule_source,LabelScheduleSource(cv_broker)],
)

View File

@ -0,0 +1,8 @@
{
"hash": "4acff9bb",
"configHash": "53a8a5ec",
"lockfileHash": "fac4afd8",
"browserHash": "5b5a9255",
"optimized": {},
"chunks": {}
}

View File

@ -0,0 +1,3 @@
{
"type": "module"
}

View File

@ -0,0 +1,2 @@
import{r as n,j as s,I as c,a9 as i}from"./index-BTPmxtwM.js";function d(){const[e,a]=n.useState(0);return s.jsxs("div",{className:"page",children:[s.jsxs("div",{className:"page-head",children:[s.jsxs("div",{children:[s.jsx("h1",{className:"page-title",children:"AI Assistant"}),s.jsx("p",{className:"page-sub",children:"Your recruiting copilot — powered by AI (interface preview)"})]}),s.jsxs("div",{className:"page-head-actions",children:[s.jsxs("span",{className:"integration-status pending",children:[s.jsx("span",{className:"pulse"}),"Model endpoint · Not connected"]}),s.jsxs("button",{className:"btn btn-secondary",onClick:()=>a(t=>t+1),children:[s.jsx(c,{name:"plus"})," New Chat"]})]})]}),s.jsx("div",{className:"card",children:s.jsx("div",{className:"card-body",children:s.jsx(i,{resetKey:e})})})]})}export{d as default};
//# sourceMappingURL=AiAssistant-B1ZQ2wQY.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"AiAssistant-B1ZQ2wQY.js","sources":["../../src/screens/AiAssistant.jsx"],"sourcesContent":["import { useState } from 'react'\r\nimport Chat from '../app/ai/Chat'\r\nimport { Icon } from '../ui/primitives'\r\n\r\nexport default function AiAssistant() {\r\n // Bumping the key resets the transcript — the old AI.newChat().\r\n const [resetKey, setResetKey] = useState(0)\r\n\r\n return (\r\n <div className=\"page\">\r\n <div className=\"page-head\">\r\n <div>\r\n <h1 className=\"page-title\">AI Assistant</h1>\r\n <p className=\"page-sub\">Your recruiting copilot — powered by AI (interface preview)</p>\r\n </div>\r\n <div className=\"page-head-actions\">\r\n <span className=\"integration-status pending\">\r\n <span className=\"pulse\" />Model endpoint · Not connected\r\n </span>\r\n <button className=\"btn btn-secondary\" onClick={() => setResetKey((k) => k + 1)}>\r\n <Icon name=\"plus\" /> New Chat\r\n </button>\r\n </div>\r\n </div>\r\n <div className=\"card\">\r\n <div className=\"card-body\">\r\n <Chat resetKey={resetKey} />\r\n </div>\r\n </div>\r\n </div>\r\n )\r\n}\r\n"],"names":["AiAssistant","resetKey","setResetKey","useState","jsxs","jsx","k","Icon","Chat"],"mappings":"8DAIA,SAAwBA,GAAc,CAEpC,KAAM,CAACC,EAAUC,CAAW,EAAIC,EAAAA,SAAS,CAAC,EAE1C,OACEC,EAAAA,KAAC,MAAA,CAAI,UAAU,OACb,SAAA,CAAAA,EAAAA,KAAC,MAAA,CAAI,UAAU,YACb,SAAA,CAAAA,OAAC,MAAA,CACC,SAAA,CAAAC,EAAAA,IAAC,KAAA,CAAG,UAAU,aAAa,SAAA,eAAY,EACvCA,EAAAA,IAAC,IAAA,CAAE,UAAU,WAAW,SAAA,6DAAA,CAA2D,CAAA,EACrF,EACAD,EAAAA,KAAC,MAAA,CAAI,UAAU,oBACb,SAAA,CAAAA,EAAAA,KAAC,OAAA,CAAK,UAAU,6BACd,SAAA,CAAAC,EAAAA,IAAC,OAAA,CAAK,UAAU,OAAA,CAAQ,EAAE,gCAAA,EAC5B,EACAD,EAAAA,KAAC,SAAA,CAAO,UAAU,oBAAoB,QAAS,IAAMF,EAAaI,GAAMA,EAAI,CAAC,EAC3E,SAAA,CAAAD,EAAAA,IAACE,EAAA,CAAK,KAAK,MAAA,CAAO,EAAE,WAAA,CAAA,CACtB,CAAA,CAAA,CACF,CAAA,EACF,EACAF,EAAAA,IAAC,MAAA,CAAI,UAAU,OACb,SAAAA,EAAAA,IAAC,MAAA,CAAI,UAAU,YACb,SAAAA,MAACG,EAAA,CAAK,SAAAP,CAAA,CAAoB,CAAA,CAC5B,CAAA,CACF,CAAA,EACF,CAEJ"}

View File

@ -0,0 +1,12 @@
import{d,r as c,af as n,j as e,I as t,B as o}from"./index-BTPmxtwM.js";import{M as m}from"./Modal-B8aWFZt2.js";function p(){const{toast:l}=d(),[a,i]=c.useState(null),r=n.filter(s=>s.status==="Beta").length;return e.jsxs("div",{className:"page",children:[e.jsxs("div",{className:"page-head",children:[e.jsxs("div",{children:[e.jsx("h1",{className:"page-title",children:"AI Studio"}),e.jsx("p",{className:"page-sub",children:"Next-generation AI modules — designed and API-ready for backend integration"})]}),e.jsx("div",{className:"page-head-actions",children:e.jsxs("span",{className:"integration-status pending",children:[e.jsx("span",{className:"pulse"}),r," in Beta"]})})]}),e.jsx("div",{className:"card brand-hero mb-18",children:e.jsxs("div",{className:"card-body",style:{display:"flex",alignItems:"center",gap:20,flexWrap:"wrap"},children:[e.jsx("div",{className:"ai-logo",style:{margin:0,width:56,height:56},children:e.jsx(t,{name:"sparkles"})}),e.jsxs("div",{style:{flex:1,minWidth:220},children:[e.jsx("h2",{style:{fontSize:19,marginBottom:4},children:"Everything is API-ready"}),e.jsx("p",{style:{opacity:.88},children:"Each module below ships with a complete, production-grade interface. Connect your model endpoint to activate them — no UI work required."})]}),e.jsxs("button",{className:"btn btn-on-brand",onClick:()=>l("Integration guide opened","info"),children:[e.jsx(t,{name:"external"})," Integration Guide"]})]})}),e.jsx("div",{className:"grid g-3",children:n.map(s=>e.jsx("div",{className:"card",style:{cursor:"pointer"},onClick:()=>i(s),children:e.jsxs("div",{className:"card-body",children:[e.jsxs("div",{className:"flex items-center",style:{justifyContent:"space-between",marginBottom:12},children:[e.jsx("span",{className:`kpi-icn ${s.cls}`,style:{width:46,height:46,borderRadius:13},children:e.jsx(t,{name:s.icon})}),e.jsx(o,{className:s.status==="Beta"?"b-indigo":"b-gray",children:s.status})]}),e.jsx("div",{className:"lr-title",style:{fontSize:15},children:s.name}),e.jsx("div",{className:"lr-sub",style:{marginTop:5,lineHeight:1.5},children:s.desc}),e.jsxs("div",{style:{marginTop:14,color:"var(--primary)",fontWeight:600,fontSize:13},children:[s.status==="Beta"?"Try it":"Join waitlist"," ",e.jsx(t,{name:"arrow-right"})]})]})},s.name))}),a&&e.jsxs(m,{title:a.name,subtitle:`${a.status} · AI Module`,size:"modal-lg",onClose:()=>i(null),footer:e.jsx("button",{className:"btn btn-secondary",onClick:()=>i(null),children:"Close"}),children:[e.jsxs("div",{className:"flex items-center gap-16",style:{marginBottom:18},children:[e.jsx("span",{className:`kpi-icn ${a.cls}`,style:{width:56,height:56,borderRadius:16},children:e.jsx(t,{name:a.icon})}),e.jsxs("div",{children:[e.jsx("div",{className:"fw-600",style:{fontSize:16},children:a.name}),e.jsx("div",{className:"text-muted",children:a.desc})]})]}),e.jsx("div",{className:"card",style:{boxShadow:"none",background:"var(--bg-sunken)"},children:e.jsxs("div",{className:"card-body",children:[e.jsx("div",{className:"form-section-title",style:{marginTop:0},children:"API Contract (preview)"}),e.jsx("pre",{className:"resume-thumb",style:{maxHeight:"none"},children:`POST /api/ai/${a.name.toLowerCase().replace(/ /g,"-")}
{
"context": { "jobId": "JOB-1001", "candidateIds": [...] },
"options": { "model": "claude-opus", "stream": true }
}
200 OK
{
"result": { ... },
"usage": { "tokens": 1240 }
}`})]})}),e.jsxs("p",{className:"text-muted text-sm",style:{marginTop:14},children:[e.jsx(t,{name:"lock"})," This features UI is complete. Backend wiring is the only remaining step."]})]})]})}export{p as default};
//# sourceMappingURL=AiStudio-CIA10Amn.js.map

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,2 @@
import{u as b,a as v,s as S,r as y,T as m,j as e,I as x,A as w}from"./index-BTPmxtwM.js";const D={"Phone Screen":"b-blue",Technical:"b-indigo","System Design":"b-purple","Onsite Loop":"b-teal","Hiring Manager":"b-amber","Culture Fit":"b-green","Final Round":"b-red"},f=["Sun","Mon","Tue","Wed","Thu","Fri","Sat"];function C(l,i){const r=new Date(l,i,1).getDay(),c=new Date(l,i+1,0).getDate(),u=new Date(l,i,0).getDate(),s=[];for(let t=r-1;t>=0;t--)s.push({day:u-t,other:!0});for(let t=1;t<=c;t++)s.push({day:t,other:!1,date:new Date(l,i,t)});for(;s.length%7!==0||s.length<42;)s.push({day:s.length-c-r+1,other:!0});return s.slice(0,42)}function I(){const l=b(),{data:i=[]}=v(S("interviews")),[{year:r,month:c},u]=y.useState({year:m.getFullYear(),month:m.getMonth()}),s=y.useMemo(()=>C(r,c),[r,c]),t=new Date(r,c).toLocaleDateString("en-US",{month:"long",year:"numeric"}),g=m.toDateString(),j=i.filter(a=>a.when.toDateString()===g),p=a=>u(({year:d,month:o})=>{const h=o+a;return h<0?{year:d-1,month:11}:h>11?{year:d+1,month:0}:{year:d,month:h}}),N=a=>l("/candidates",{state:{openCandidate:a}});return e.jsxs("div",{className:"page",children:[e.jsxs("div",{className:"page-head",children:[e.jsxs("div",{children:[e.jsx("h1",{className:"page-title",children:"Calendar"}),e.jsx("p",{className:"page-sub",children:"Interview schedule at a glance"})]}),e.jsxs("div",{className:"page-head-actions",children:[e.jsxs("div",{className:"flex items-center gap-8",children:[e.jsx("button",{className:"btn btn-icon btn-secondary",onClick:()=>p(-1),"aria-label":"Previous month",children:e.jsx(x,{name:"chevron-left"})}),e.jsx("span",{className:"fw-600",style:{minWidth:140,textAlign:"center"},children:t}),e.jsx("button",{className:"btn btn-icon btn-secondary",onClick:()=>p(1),"aria-label":"Next month",children:e.jsx(x,{name:"chevron-right"})})]}),e.jsxs("button",{className:"btn btn-primary",onClick:()=>l("/interviews",{state:{openSchedule:!0}}),children:[e.jsx(x,{name:"plus"})," Schedule"]})]})]}),e.jsxs("div",{className:"grid g-2-1",children:[e.jsx("div",{className:"card",children:e.jsx("div",{className:"card-body",children:e.jsxs("div",{className:"cal-grid",children:[f.map(a=>e.jsx("div",{className:"cal-dow",children:a},a)),s.map((a,d)=>{const o=!a.other&&a.date?i.filter(n=>n.when.toDateString()===a.date.toDateString()):[],h=!a.other&&a.date&&a.date.toDateString()===g;return e.jsxs("div",{className:`cal-cell ${a.other?"other":""} ${h?"today":""}`,children:[e.jsx("div",{className:"cal-date",children:a.day}),o.slice(0,3).map(n=>e.jsxs("div",{className:`cal-event ${D[n.type]||"b-blue"}`,title:`${n.candidate} · ${n.type}`,onClick:()=>N(n.candidateId),children:[n.when.toLocaleTimeString("en-US",{hour:"numeric"})," ",n.candidate.split(" ")[0]]},n.id)),o.length>3&&e.jsxs("div",{className:"cal-event b-gray",children:["+",o.length-3," more"]})]},d)})]})})}),e.jsxs("div",{className:"card",style:{alignSelf:"start"},children:[e.jsx("div",{className:"card-head",children:e.jsxs("div",{children:[e.jsx("h3",{children:"Today"}),e.jsx("span",{className:"ch-sub",children:m.toLocaleDateString("en-US",{month:"long",day:"numeric",year:"numeric"})})]})}),e.jsx("div",{className:"card-body",children:e.jsx("div",{className:"list-tight",children:j.length===0?e.jsx("p",{className:"text-muted",children:"No interviews today"}):j.map(a=>e.jsxs("div",{className:"list-row",style:{cursor:"pointer"},onClick:()=>N(a.candidateId),children:[e.jsx(w,{name:a.candidate,initials:a.candInitials,color:a.color}),e.jsxs("div",{className:"lr-main",children:[e.jsx("div",{className:"lr-title",children:a.candidate}),e.jsx("div",{className:"lr-sub",children:a.type})]}),e.jsx("div",{className:"lr-right",children:e.jsx("div",{className:"fw-600 text-sm",children:a.when.toLocaleTimeString("en-US",{hour:"numeric",minute:"2-digit"})})})]},a.id))})})]})]})]})}export{I as default};
//# sourceMappingURL=Calendar-C28nSZlh.js.map

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,2 @@
import{j as e,E as k,r as f,I as p}from"./index-BTPmxtwM.js";function N({columns:l,rows:i,pageSize:r=10}){const[t,o]=f.useState({key:null,dir:1}),[s,d]=f.useState(1);f.useEffect(()=>d(1),[i]);const a=f.useMemo(()=>{if(!t.key)return i;const n=l.find(c=>c.key===t.key);return[...i].sort((c,m)=>{let u=n!=null&&n.sortValue?n.sortValue(c):c[t.key],j=n!=null&&n.sortValue?n.sortValue(m):m[t.key];return typeof u=="string"&&(u=u.toLowerCase(),j=(j||"").toLowerCase()),u<j?-1*t.dir:u>j?1*t.dir:0})},[i,l,t]),h=a.length,g=Math.max(1,Math.ceil(h/r)),b=Math.min(s,g),x=(b-1)*r;function y(n){o(c=>c.key===n?{key:n,dir:c.dir*-1}:{key:n,dir:1})}return{pageRows:a.slice(x,x+r),sort:t,toggleSort:y,page:b,pages:g,setPage:d,from:h?x+1:0,to:Math.min(x+r,h),total:h,pageButtons:v(b,g)}}function v(l,i){const r=[];for(let t=1;t<=i;t++)t===1||t===i||Math.abs(t-l)<=1?r.push(t):r[r.length-1]!=="…"&&r.push("…");return r}function C({from:l,to:i,total:r,page:t,pages:o,setPage:s,pageButtons:d}){return e.jsxs("div",{className:"pagination",children:[e.jsxs("span",{className:"page-info",children:["Showing ",e.jsxs("b",{children:[l,"",i]})," of ",e.jsx("b",{children:r})]}),e.jsxs("div",{className:"page-controls",children:[e.jsx("button",{className:"page-btn",disabled:t===1,onClick:()=>s(t-1),"aria-label":"Previous page",children:e.jsx(p,{name:"chevron-left"})}),d.map((a,h)=>a==="…"?e.jsx("span",{className:"page-btn",style:{cursor:"default"},children:"…"},`gap-${h}`):e.jsx("button",{className:`page-btn ${a===t?"active":""}`,onClick:()=>s(a),"aria-current":a===t?"page":void 0,children:a},a)),e.jsx("button",{className:"page-btn",disabled:t===o,onClick:()=>s(t+1),"aria-label":"Next page",children:e.jsx(p,{name:"chevron-right"})})]})]})}function S({columns:l,rows:i,pageSize:r=10,empty:t}){const o=N({columns:l,rows:i,pageSize:r});return e.jsxs("div",{className:"dt",children:[e.jsx("div",{className:"table-wrap",children:e.jsxs("table",{className:"data",children:[e.jsx("thead",{children:e.jsx("tr",{children:l.map(s=>{const d=o.sort.key===s.key,a=[s.sortable?"sortable":"",d?o.sort.dir===1?"sorted-asc":"sorted-desc":""].filter(Boolean).join(" ");return e.jsxs("th",{className:a,style:{textAlign:s.align||"left"},onClick:s.sortable?()=>o.toggleSort(s.key):void 0,children:[s.label,s.sortable&&e.jsx("span",{className:"sort-ind",children:d?o.sort.dir===1?"▲":"▼":"⇅"})]},s.key)})})}),e.jsx("tbody",{children:o.pageRows.length===0?e.jsx("tr",{children:e.jsx("td",{colSpan:l.length,children:e.jsx(k,{children:t})})}):o.pageRows.map((s,d)=>e.jsx("tr",{children:l.map(a=>e.jsx("td",{style:{textAlign:a.align||"left"},children:a.render?a.render(s):s[a.key]??""},a.key))},s.id??d))})]})}),e.jsx(C,{...o})]})}export{S as D,C as P,N as u};
//# sourceMappingURL=DataTable-D5imKbZq.js.map

File diff suppressed because one or more lines are too long

2
frontend/dist/assets/Help-CrblBaFq.js vendored Normal file
View File

@ -0,0 +1,2 @@
import{d as c,r as d,j as e,I as i}from"./index-BTPmxtwM.js";const l=[{q:"How do I create a new job requisition?",a:'Navigate to Jobs and click "Create Job". Fill in the required fields marked with an asterisk and click Save. The job will immediately appear in your listings.'},{q:"How does the AI candidate score work?",a:"The AI score (0100) evaluates how well a candidate matches the job requirements based on skills, experience, and education. Higher scores indicate stronger matches."},{q:"Can I move candidates between pipeline stages?",a:"Yes. Open the Pipeline view and simply drag any candidate card between stage columns. The candidates status updates automatically."},{q:"How do I schedule an interview?",a:'Go to Interviews or Calendar and click "Schedule Interview". Select the candidate, round, date, time, and interviewers.'},{q:"How do I export reports?",a:'On the Reports page, use the "Export Report" button for a full PDF, or the CSV buttons on individual tables.'}],o=[{icn:"file",t:"Documentation",d:"Complete product guides",cls:"i-indigo"},{icn:"video",t:"Video Tutorials",d:"Watch step-by-step walkthroughs",cls:"i-red"},{icn:"message",t:"Live Chat",d:"Chat with our support team",cls:"i-green"},{icn:"users",t:"Community",d:"Connect with other recruiters",cls:"i-purple"}];function p(){const{toast:n}=c(),[t,r]=d.useState(null);return e.jsxs("div",{className:"page",children:[e.jsx("div",{className:"page-head",children:e.jsxs("div",{children:[e.jsx("h1",{className:"page-title",children:"Help Center"}),e.jsx("p",{className:"page-sub",children:"Find answers and get support"})]})}),e.jsx("div",{className:"card brand-hero mb-18",children:e.jsxs("div",{className:"card-body",style:{padding:32,textAlign:"center"},children:[e.jsx("h2",{style:{fontSize:22,marginBottom:8},children:"How can we help you?"}),e.jsx("p",{style:{opacity:.85,marginBottom:18},children:"Search our knowledge base or browse the topics below"}),e.jsxs("div",{className:"topbar-search",style:{maxWidth:480,margin:"0 auto"},children:[e.jsx(i,{name:"search"}),e.jsx("input",{placeholder:"Search help articles…"})]})]})}),e.jsx("div",{className:"grid g-kpi mb-18",children:o.map(s=>e.jsx("div",{className:"card",style:{cursor:"pointer"},onClick:()=>n(`Opening ${s.t}`,"info"),children:e.jsxs("div",{className:"card-body",style:{textAlign:"center"},children:[e.jsx("span",{className:`kpi-icn ${s.cls}`,style:{margin:"0 auto 12px",width:48,height:48,borderRadius:14},children:e.jsx(i,{name:s.icn})}),e.jsx("div",{className:"fw-600",children:s.t}),e.jsx("div",{className:"lr-sub",style:{marginTop:4},children:s.d})]})},s.t))}),e.jsxs("div",{className:"card",children:[e.jsx("div",{className:"card-head",children:e.jsx("div",{children:e.jsx("h3",{children:"Frequently Asked Questions"})})}),e.jsx("div",{className:"card-body",children:l.map((s,a)=>e.jsxs("div",{className:"setting-row",style:{cursor:"pointer",flexDirection:"column",alignItems:"stretch"},onClick:()=>r(t===a?null:a),children:[e.jsxs("div",{className:"flex items-center",style:{justifyContent:"space-between"},children:[e.jsx("h4",{children:s.q}),e.jsx("span",{style:{color:"var(--text-3)",transition:".2s",transform:t===a?"rotate(90deg)":"rotate(0deg)"},children:e.jsx(i,{name:"chevron-right"})})]}),t===a&&e.jsx("p",{style:{marginTop:10},children:s.a})]},s.q))})]})]})}export{p as default};
//# sourceMappingURL=Help-CrblBaFq.js.map

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

4
frontend/dist/assets/Jobs-cmKetLfV.js vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,2 @@
import{r as l,av as N,j as t,I as g}from"./index-BTPmxtwM.js";let c=0;function w(){c+=1,document.body.style.overflow="hidden"}function R(){c=Math.max(0,c-1),c===0&&(document.body.style.overflow="")}const j='a[href], button:not([disabled]), textarea:not([disabled]), input:not([disabled]), select:not([disabled]), [tabindex]:not([tabindex="-1"])';function L({open:d=!0,title:i,subtitle:u,size:p,footer:f,onClose:s,children:E}){const m=l.useRef(null),x=l.useRef(null),n=l.useCallback(()=>s==null?void 0:s(),[s]);return l.useEffect(()=>{var a,b;if(!d)return;x.current=document.activeElement,w();const r=m.current;(b=(a=(r==null?void 0:r.querySelector(j))??r)==null?void 0:a.focus)==null||b.call(a);const v=e=>{if(e.key==="Escape"){e.stopPropagation(),n();return}if(e.key!=="Tab"||!r)return;const o=Array.from(r.querySelectorAll(j)).filter(k=>k.offsetParent!==null);if(!o.length)return;const h=o[0],y=o[o.length-1];e.shiftKey&&document.activeElement===h?(e.preventDefault(),y.focus()):!e.shiftKey&&document.activeElement===y&&(e.preventDefault(),h.focus())};return document.addEventListener("keydown",v,!0),()=>{var e,o;document.removeEventListener("keydown",v,!0),R(),(o=(e=x.current)==null?void 0:e.focus)==null||o.call(e)}},[d,n]),d?N.createPortal(t.jsxs("div",{className:"modal-root open",children:[t.jsx("div",{className:"modal-backdrop",onClick:n}),t.jsxs("div",{className:`modal ${p||""}`,role:"dialog","aria-modal":"true","aria-label":typeof i=="string"?i:void 0,tabIndex:-1,ref:m,children:[t.jsxs("div",{className:"modal-head",children:[t.jsxs("div",{children:[t.jsx("h2",{children:i}),u&&t.jsx("p",{children:u})]}),t.jsx("button",{className:"modal-close",onClick:n,"aria-label":"Close",children:t.jsx(g,{name:"x"})})]}),t.jsx("div",{className:"modal-body",children:E}),f&&t.jsx("div",{className:"modal-foot",children:f})]})]}),document.body):null}export{L as M};
//# sourceMappingURL=Modal-B8aWFZt2.js.map

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,2 @@
import{d as m,a as x,s as h,g as j,j as s,I as i}from"./index-BTPmxtwM.js";function u(){const{toast:t}=m(),{data:o=[]}=x(h("notifications")),n=j("notifications"),l=a=>n(e=>e.map((c,r)=>r===a?{...c,unread:!1}:c)),d=()=>{n(a=>a.map(e=>({...e,unread:!1}))),t("All notifications marked as read","success")};return s.jsxs("div",{className:"page",children:[s.jsxs("div",{className:"page-head",children:[s.jsxs("div",{children:[s.jsx("h1",{className:"page-title",children:"Notifications"}),s.jsx("p",{className:"page-sub",children:"Stay on top of hiring activity"})]}),s.jsxs("div",{className:"page-head-actions",children:[s.jsxs("button",{className:"btn btn-secondary",onClick:d,children:[s.jsx(i,{name:"check"})," Mark all read"]}),s.jsx("button",{className:"btn btn-ghost",onClick:()=>t("Notification settings","info"),children:s.jsx(i,{name:"more"})})]})]}),s.jsx("div",{className:"card",children:s.jsx("div",{className:"list-tight",style:{padding:0},children:o.map((a,e)=>s.jsxs("div",{className:`notif-row${a.unread?" unread":""}`,onClick:()=>l(e),children:[s.jsx("span",{className:`notif-icn ${a.color}`,children:s.jsx(i,{name:a.icon})}),s.jsxs("div",{className:"notif-body",children:[s.jsx("div",{className:"notif-title",children:a.title}),s.jsx("div",{className:"notif-text",children:a.text}),s.jsx("div",{className:"notif-time",children:a.time})]}),a.unread&&s.jsx("span",{className:"dot dot-blue",style:{position:"static",border:"none",alignSelf:"center"}})]},a.id??`${a.title}-${e}`))})})]})}export{u as default};
//# sourceMappingURL=Notifications-C-babC9D.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"Notifications-C-babC9D.js","sources":["../../src/screens/Notifications.jsx"],"sourcesContent":["import { useQuery } from '@tanstack/react-query'\r\nimport { Icon } from '../ui/primitives'\r\nimport { useToast } from '../ui/Toast'\r\nimport { seedQuery, useSeedMutation } from '../data/seedQueries'\r\n\r\nexport default function Notifications() {\r\n const { toast } = useToast()\r\n const { data: notifications = [] } = useQuery(seedQuery('notifications'))\r\n const update = useSeedMutation('notifications')\r\n\r\n // Marking one read used to be `this.classList.remove('unread')` — a DOM edit\r\n // the badge count never saw. Writing to the cache keeps the sidebar in sync.\r\n const markOne = (i) => update((ns) => ns.map((n, j) => (j === i ? { ...n, unread: false } : n)))\r\n const markAll = () => {\r\n update((ns) => ns.map((n) => ({ ...n, unread: false })))\r\n toast('All notifications marked as read', 'success')\r\n }\r\n\r\n return (\r\n <div className=\"page\">\r\n <div className=\"page-head\">\r\n <div>\r\n <h1 className=\"page-title\">Notifications</h1>\r\n <p className=\"page-sub\">Stay on top of hiring activity</p>\r\n </div>\r\n <div className=\"page-head-actions\">\r\n <button className=\"btn btn-secondary\" onClick={markAll}><Icon name=\"check\" /> Mark all read</button>\r\n <button className=\"btn btn-ghost\" onClick={() => toast('Notification settings', 'info')}>\r\n <Icon name=\"more\" />\r\n </button>\r\n </div>\r\n </div>\r\n\r\n <div className=\"card\">\r\n <div className=\"list-tight\" style={{ padding: 0 }}>\r\n {notifications.map((n, i) => (\r\n <div\r\n key={n.id ?? `${n.title}-${i}`}\r\n className={`notif-row${n.unread ? ' unread' : ''}`}\r\n onClick={() => markOne(i)}\r\n >\r\n <span className={`notif-icn ${n.color}`}><Icon name={n.icon} /></span>\r\n <div className=\"notif-body\">\r\n <div className=\"notif-title\">{n.title}</div>\r\n <div className=\"notif-text\">{n.text}</div>\r\n <div className=\"notif-time\">{n.time}</div>\r\n </div>\r\n {n.unread && (\r\n <span className=\"dot dot-blue\" style={{ position: 'static', border: 'none', alignSelf: 'center' }} />\r\n )}\r\n </div>\r\n ))}\r\n </div>\r\n </div>\r\n </div>\r\n )\r\n}\r\n"],"names":["Notifications","toast","useToast","notifications","useQuery","seedQuery","update","useSeedMutation","markOne","i","ns","n","j","markAll","jsxs","jsx","Icon"],"mappings":"2EAKA,SAAwBA,GAAgB,CACtC,KAAM,CAAE,MAAAC,CAAA,EAAUC,EAAA,EACZ,CAAE,KAAMC,EAAgB,CAAA,GAAOC,EAASC,EAAU,eAAe,CAAC,EAClEC,EAASC,EAAgB,eAAe,EAIxCC,EAAWC,GAAMH,EAAQI,GAAOA,EAAG,IAAI,CAACC,EAAGC,IAAOA,IAAMH,EAAI,CAAE,GAAGE,EAAG,OAAQ,EAAA,EAAUA,CAAE,CAAC,EACzFE,EAAU,IAAM,CACpBP,EAAQI,GAAOA,EAAG,IAAKC,IAAO,CAAE,GAAGA,EAAG,OAAQ,EAAA,EAAQ,CAAC,EACvDV,EAAM,mCAAoC,SAAS,CACrD,EAEA,OACEa,EAAAA,KAAC,MAAA,CAAI,UAAU,OACb,SAAA,CAAAA,EAAAA,KAAC,MAAA,CAAI,UAAU,YACb,SAAA,CAAAA,OAAC,MAAA,CACC,SAAA,CAAAC,EAAAA,IAAC,KAAA,CAAG,UAAU,aAAa,SAAA,gBAAa,EACxCA,EAAAA,IAAC,IAAA,CAAE,UAAU,WAAW,SAAA,gCAAA,CAA8B,CAAA,EACxD,EACAD,EAAAA,KAAC,MAAA,CAAI,UAAU,oBACb,SAAA,CAAAA,EAAAA,KAAC,SAAA,CAAO,UAAU,oBAAoB,QAASD,EAAS,SAAA,CAAAE,EAAAA,IAACC,EAAA,CAAK,KAAK,OAAA,CAAQ,EAAE,gBAAA,EAAc,EAC3FD,EAAAA,IAAC,SAAA,CAAO,UAAU,gBAAgB,QAAS,IAAMd,EAAM,wBAAyB,MAAM,EACpF,SAAAc,MAACC,EAAA,CAAK,KAAK,OAAO,CAAA,CACpB,CAAA,CAAA,CACF,CAAA,EACF,QAEC,MAAA,CAAI,UAAU,OACb,SAAAD,EAAAA,IAAC,OAAI,UAAU,aAAa,MAAO,CAAE,QAAS,CAAA,EAC3C,WAAc,IAAI,CAACJ,EAAGF,IACrBK,EAAAA,KAAC,MAAA,CAEC,UAAW,YAAYH,EAAE,OAAS,UAAY,EAAE,GAChD,QAAS,IAAMH,EAAQC,CAAC,EAExB,SAAA,CAAAM,EAAAA,IAAC,OAAA,CAAK,UAAW,aAAaJ,EAAE,KAAK,GAAI,SAAAI,EAAAA,IAACC,EAAA,CAAK,KAAML,EAAE,IAAA,CAAM,EAAE,EAC/DG,EAAAA,KAAC,MAAA,CAAI,UAAU,aACb,SAAA,CAAAC,EAAAA,IAAC,MAAA,CAAI,UAAU,cAAe,SAAAJ,EAAE,MAAM,EACtCI,EAAAA,IAAC,MAAA,CAAI,UAAU,aAAc,WAAE,KAAK,EACpCA,EAAAA,IAAC,MAAA,CAAI,UAAU,aAAc,WAAE,IAAA,CAAK,CAAA,EACtC,EACCJ,EAAE,QACDI,EAAAA,IAAC,OAAA,CAAK,UAAU,eAAe,MAAO,CAAE,SAAU,SAAU,OAAQ,OAAQ,UAAW,SAAS,CAAG,CAAA,CAAA,EAXhGJ,EAAE,IAAM,GAAGA,EAAE,KAAK,IAAIF,CAAC,EAAA,CAc/B,EACH,CAAA,CACF,CAAA,EACF,CAEJ"}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,2 @@
import{d as A,u as D,a as p,s as v,g as C,r,j as e,I,A as y,S as T}from"./index-BTPmxtwM.js";const j=[{name:"Applied",color:"var(--stage-1)"},{name:"Screening",color:"var(--stage-2)"},{name:"Assessment",color:"var(--stage-3)"},{name:"Interview",color:"var(--stage-4)"},{name:"Offer",color:"var(--stage-5)"},{name:"Hired",color:"var(--stage-6)"},{name:"Rejected",color:"var(--stage-7)"}];function O(){const{toast:h}=A(),u=D(),{data:l=[]}=p(v("candidates")),{data:x=[]}=p(v("jobs")),N=C("candidates"),[d,f]=r.useState(""),[c,o]=r.useState(null),[b,i]=r.useState(null),g=r.useMemo(()=>d?l.filter(a=>a.jobId===d):l,[l,d]),k=r.useMemo(()=>{const a=Object.fromEntries(j.map(n=>[n.name,[]]));for(const n of g)a[n.stage]&&a[n.stage].push(n);return a},[g]);function S(a){i(null);const n=c;if(o(null),!n)return;const s=l.find(t=>t.id===n);!s||s.stage===a||(N(t=>t.map(m=>m.id===n?{...m,stage:a,status:a}:m)),h(`${s.name} moved to ${a}`,"success"))}return e.jsxs("div",{className:"page",children:[e.jsxs("div",{className:"page-head",children:[e.jsxs("div",{children:[e.jsx("h1",{className:"page-title",children:"Pipeline"}),e.jsx("p",{className:"page-sub",children:"Drag candidates between stages to update their status"})]}),e.jsxs("div",{className:"page-head-actions",children:[e.jsxs("select",{className:"select",value:d,onChange:a=>f(a.target.value),children:[e.jsx("option",{value:"",children:"All Jobs"}),x.filter(a=>a.status==="Open").map(a=>e.jsx("option",{value:a.id,children:a.title},a.id))]}),e.jsxs("button",{className:"btn btn-primary",onClick:()=>u("/candidates",{state:{openAdd:!0}}),children:[e.jsx(I,{name:"plus"})," Add Candidate"]})]})]}),e.jsx("div",{className:"kanban",children:j.map(a=>{const n=k[a.name]??[];return e.jsxs("div",{className:"kanban-col",children:[e.jsxs("div",{className:"kanban-col-head",children:[e.jsx("span",{className:"k-dot",style:{background:a.color}}),e.jsx("h4",{children:a.name}),e.jsx("span",{className:"k-count",children:n.length})]}),e.jsx("div",{className:`kanban-cards${b===a.name?" drag-over":""}`,onDragOver:s=>{s.preventDefault(),i(a.name)},onDragLeave:()=>i(s=>s===a.name?null:s),onDrop:s=>{s.preventDefault(),S(a.name)},children:n.map(s=>e.jsxs("div",{className:`k-card${c===s.id?" dragging":""}`,draggable:!0,onDragStart:t=>{o(s.id),t.dataTransfer.effectAllowed="move",t.dataTransfer.setData("text/plain",s.id)},onDragEnd:()=>{o(null),i(null)},onClick:()=>{c||u("/candidates",{state:{openCandidate:s.id}})},children:[e.jsxs("div",{className:"k-card-top",children:[e.jsx(y,{name:s.name,initials:s.initials,color:s.color}),e.jsxs("div",{children:[e.jsx("div",{className:"kc-name",children:s.name}),e.jsx("div",{className:"kc-role",children:s.currentTitle})]})]}),e.jsx("div",{className:"kc-role",children:s.jobTitle}),e.jsx("div",{className:"k-tags",children:s.skills.slice(0,3).map(t=>e.jsx("span",{className:"tag",children:t},t))}),e.jsxs("div",{className:"k-card-meta",children:[e.jsx("span",{className:"cell-sub",children:s.currentCompany}),e.jsx(T,{score:s.aiScore})]})]},s.id))})]},a.name)})})]})}export{j as KANBAN_STAGES,O as default};
//# sourceMappingURL=Pipeline-Bhzm15UM.js.map

File diff suppressed because one or more lines are too long

2
frontend/dist/assets/Rbac-JCKSk_o7.js vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

2
frontend/dist/assets/Tabs-DVZeUemd.js vendored Normal file
View File

@ -0,0 +1,2 @@
import{r as u,j as e}from"./index-BTPmxtwM.js";function d({tabs:n,value:c,onChange:o,className:r="tabs"}){const l=u.useId();return e.jsx("div",{className:r,role:"tablist",children:n.map(s=>{const a=s.key??s,i=s.label??s,t=a===c;return e.jsxs("button",{id:`${l}-${a}`,role:"tab","aria-selected":t,className:`tab${t?" active":""}`,onClick:()=>o(a),children:[i,s.count!=null&&e.jsx("span",{className:"tab-count",children:s.count})]},a)})})}export{d as T};
//# sourceMappingURL=Tabs-DVZeUemd.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"Tabs-DVZeUemd.js","sources":["../../src/ui/Tabs.jsx"],"sourcesContent":["/* ============================================================\r\n Tabs.jsx — new primitive.\r\n\r\n js/ui.js had no tab component, so settings (10 tabs), candidates (8), inbox\r\n (7) and rbac each hand-rolled one by pre-rendering every pane and toggling\r\n `.active`. One component replaces four ad-hoc implementations, and only the\r\n active pane is mounted — which also means a chart in a hidden pane no longer\r\n draws into a zero-width canvas.\r\n ============================================================ */\r\n\r\nimport { useId, useState } from 'react'\r\n\r\nexport function Tabs({ tabs, value, onChange, className = 'tabs' }) {\r\n const id = useId()\r\n return (\r\n <div className={className} role=\"tablist\">\r\n {tabs.map((t) => {\r\n const key = t.key ?? t\r\n const label = t.label ?? t\r\n const active = key === value\r\n return (\r\n <button\r\n key={key}\r\n id={`${id}-${key}`}\r\n role=\"tab\"\r\n aria-selected={active}\r\n className={`tab${active ? ' active' : ''}`}\r\n onClick={() => onChange(key)}\r\n >\r\n {label}\r\n {t.count != null && <span className=\"tab-count\">{t.count}</span>}\r\n </button>\r\n )\r\n })}\r\n </div>\r\n )\r\n}\r\n\r\n/** Uncontrolled convenience wrapper: <TabPanel tabs={[{key,label,render}]} /> */\r\nexport default function TabPanel({ tabs, initial, className }) {\r\n const [value, setValue] = useState(initial ?? tabs[0]?.key)\r\n const active = tabs.find((t) => t.key === value) ?? tabs[0]\r\n return (\r\n <>\r\n <Tabs tabs={tabs} value={value} onChange={setValue} className={className} />\r\n <div role=\"tabpanel\">{active?.render?.()}</div>\r\n </>\r\n )\r\n}\r\n"],"names":["Tabs","tabs","value","onChange","className","id","useId","jsx","t","key","label","active","jsxs"],"mappings":"+CAYO,SAASA,EAAK,CAAE,KAAAC,EAAM,MAAAC,EAAO,SAAAC,EAAU,UAAAC,EAAY,QAAU,CAClE,MAAMC,EAAKC,EAAAA,MAAA,EACX,OACEC,MAAC,OAAI,UAAAH,EAAsB,KAAK,UAC7B,SAAAH,EAAK,IAAKO,GAAM,CACf,MAAMC,EAAMD,EAAE,KAAOA,EACfE,EAAQF,EAAE,OAASA,EACnBG,EAASF,IAAQP,EACvB,OACEU,EAAAA,KAAC,SAAA,CAEC,GAAI,GAAGP,CAAE,IAAII,CAAG,GAChB,KAAK,MACL,gBAAeE,EACf,UAAW,MAAMA,EAAS,UAAY,EAAE,GACxC,QAAS,IAAMR,EAASM,CAAG,EAE1B,SAAA,CAAAC,EACAF,EAAE,OAAS,MAAQD,EAAAA,IAAC,QAAK,UAAU,YAAa,WAAE,KAAA,CAAM,CAAA,CAAA,EARpDE,CAAA,CAWX,CAAC,CAAA,CACH,CAEJ"}

View File

@ -0,0 +1,2 @@
import{d as P,a as S,s as F,g as R,r as u,j as e,I as v,Q as O,E as f,h as _,A as k,S as D,B as I,y as M,z as q,q as B}from"./index-BTPmxtwM.js";import{t as L,A as $,C as H,l as Q}from"./Candidates-DDm6mZmg.js";import"./useMutation-CMO99S-s.js";import"./Modal-B8aWFZt2.js";import"./DataTable-D5imKbZq.js";import"./Tabs-DVZeUemd.js";import"./jobPosts-CNShlpwX.js";const T=100,y=["Applied","Screening","Assessment","Interview","Offer","Hired"],w={PENDING:"Applied",CLOSED:"Applied",PROCESS:"Screening",ONHOLD:"Screening",APPROVED:"Hired",REJECTED:"Rejected"};function G(t){const a=parseInt(t,10);return Number.isFinite(a)?a:null}function z(t,a){const r=t.name||a.name,i=(t.job_posts||[]).map(h=>h.title).find(Boolean),o=w[t.application_status]||a.stage,d=G(t.experience);return{...a,userId:t.user_id,name:r,initials:q(r),color:M(r),email:t.email||a.email,experience:d??a.experience,stage:o,status:o,currentTitle:i||a.currentTitle,jobTitle:i||a.jobTitle}}function J(t,a){if(!a.length)return[];const r=new Map;for(const i of t){const o=i.user_id??`inbox-${i.inbox_id}`;r.has(o)||r.set(o,i)}return[...r.values()].map((i,o)=>z(i,a[o%a.length]))}function ee(){const{toast:t}=P(),{data:a=[]}=S(F("candidates")),r=R("candidates"),[i,o]=u.useState(""),[d,h]=u.useState(""),[N,c]=u.useState(null),[b,x]=u.useState(null),p=S({queryKey:B.candidates.list({limit:T}),queryFn:()=>Q({limit:T})}),g=u.useMemo(()=>J(L(p.data),a),[p.data,a]),C=u.useMemo(()=>g.filter(s=>!(d&&s.department!==d||i&&!(s.name+s.currentCompany+s.skills.join(" ")).toLowerCase().includes(i.toLowerCase()))),[g,i,d]);function A(s){r(n=>n.map(l=>l.id===s.id?{...l,favorite:!l.favorite}:l)),c(n=>n&&n.id===s.id?{...n,favorite:!n.favorite}:n),t(s.favorite?"Removed from favorites":`${s.name} added to favorites`,"success")}function E(s){const n=y.indexOf(s.stage);if(n===-1||n>=y.length-1){t(`${s.name} cannot be advanced further`,"warning");return}const l=y[n+1];r(m=>m.map(j=>j.id===s.id?{...j,stage:l,status:l}:j)),c(m=>m&&m.id===s.id?{...m,stage:l,status:l}:m),t(`${s.name} moved to ${l}`,"success")}return e.jsxs("div",{className:"page",children:[e.jsxs("div",{className:"page-head",children:[e.jsxs("div",{children:[e.jsx("h1",{className:"page-title",children:"Talent Pool"}),e.jsxs("p",{className:"page-sub",children:[g.length," silver-medalists & passive candidates to re-engage"]})]}),e.jsx("div",{className:"page-head-actions",children:e.jsxs("button",{className:"btn btn-primary",onClick:()=>t("Talent campaign created","success"),children:[e.jsx(v,{name:"send"})," Start Campaign"]})})]}),e.jsx("div",{className:"card mb-18",children:e.jsx("div",{className:"card-body",style:{padding:16},children:e.jsxs("div",{className:"toolbar",style:{marginBottom:0},children:[e.jsxs("div",{className:"toolbar-search",children:[e.jsx(v,{name:"search"}),e.jsx("input",{value:i,onChange:s=>o(s.target.value),placeholder:"Search by name, skill, company…"})]}),e.jsxs("select",{className:"select",value:d,onChange:s=>h(s.target.value),children:[e.jsx("option",{value:"",children:"All Departments"}),O.map(s=>e.jsx("option",{children:s},s))]})]})})}),e.jsx("div",{className:"grid g-3",children:C.length===0?e.jsx("div",{style:{gridColumn:"1/-1"},children:p.isError?e.jsx(f,{title:"Could not load talent pool",children:_(p.error,"Please try again.")}):p.isPending?e.jsx(f,{title:"Loading talent pool…",children:"Fetching candidates."}):e.jsx(f,{title:"No talent found",children:"Try a different search or department."})}):C.map(s=>e.jsx("div",{className:"card",style:{cursor:"pointer"},onClick:()=>c(s),children:e.jsxs("div",{className:"card-body",children:[e.jsxs("div",{className:"flex items-center gap-12",style:{marginBottom:12},children:[e.jsx(k,{name:s.name,initials:s.initials,color:s.color,className:"avatar-lg"}),e.jsxs("div",{style:{flex:1,minWidth:0},children:[e.jsx("div",{className:"lr-title",children:s.name}),e.jsx("div",{className:"lr-sub",children:s.currentTitle})]}),e.jsx(D,{score:s.aiScore})]}),e.jsx("div",{className:"k-tags",style:{marginBottom:12},children:s.skills.slice(0,4).map(n=>e.jsx("span",{className:"tag",children:n},n))}),e.jsx("div",{className:"divider",style:{margin:"12px 0"}}),e.jsxs("div",{className:"flex items-center",style:{justifyContent:"space-between"},children:[e.jsxs("span",{className:"cell-sub",children:[e.jsx(v,{name:"briefcase"})," ",s.experience," yrs"]}),e.jsx("span",{className:"cell-sub",children:s.currentCompany}),e.jsx(I,{className:"b-gray",children:s.source})]})]})},s.id))}),b&&e.jsx($,{candidate:b,onClose:()=>x(null),onProfile:s=>{x(null),c(s)}}),N&&e.jsx(H,{candidate:N,onClose:()=>c(null),onAdvance:E,onToggleFav:A,onAtsMatch:s=>{c(null),x(s)}})]})}export{ee as default};
//# sourceMappingURL=TalentPool-CbXp920L.js.map

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

72
frontend/dist/assets/index-BTPmxtwM.js vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,2 @@
import{a1 as i}from"./index-BTPmxtwM.js";function f({search:a,top:e,skip:t,ids:r,activeOnly:o=!0}={}){const n=Array.isArray(r)?r.filter(Boolean).join(","):r;return i("/job/fetch",{params:{search:a,top:e,skip:t,ids:n||void 0,active_only:o}})}export{f as l};
//# sourceMappingURL=jobPosts-CNShlpwX.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"jobPosts-CNShlpwX.js","sources":["../../src/api/jobPosts.js"],"sourcesContent":["import { request } from '../lib/apiClient'\r\n\r\n/**\r\n * Active job posts — Job Matching hydrates suggestions and the manual picker.\r\n *\r\n * Permissioned with job_board.view (not jobs.*). `ids` is a comma-joined list\r\n * so one round trip can resolve a whole suggestion rail.\r\n */\r\nexport function list({ search, top, skip, ids, activeOnly = true } = {}) {\r\n const idParam = Array.isArray(ids) ? ids.filter(Boolean).join(',') : ids\r\n return request('/job/fetch', {\r\n params: {\r\n search,\r\n top,\r\n skip,\r\n ids: idParam || undefined,\r\n active_only: activeOnly,\r\n },\r\n })\r\n}\r\n"],"names":["list","search","top","skip","ids","activeOnly","idParam","request"],"mappings":"yCAQO,SAASA,EAAK,CAAE,OAAAC,EAAQ,IAAAC,EAAK,KAAAC,EAAM,IAAAC,EAAK,WAAAC,EAAa,EAAI,EAAK,GAAI,CACvE,MAAMC,EAAU,MAAM,QAAQF,CAAG,EAAIA,EAAI,OAAO,OAAO,EAAE,KAAK,GAAG,EAAIA,EACrE,OAAOG,EAAQ,aAAc,CAC3B,OAAQ,CACN,OAAAN,EACA,IAAAC,EACA,KAAAC,EACA,IAAKG,GAAW,OAChB,YAAaD,CACnB,CACA,CAAG,CACH"}

View File

@ -0,0 +1,2 @@
import{a1 as e}from"./index-BTPmxtwM.js";function o(){return e("/roles/fetch")}function s(r){return e("/roles/create",{method:"POST",body:r})}export{s as c,o as l};
//# sourceMappingURL=roles-DSy_gQji.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"roles-DSy_gQji.js","sources":["../../src/api/roles.js"],"sourcesContent":["import { request } from '../lib/apiClient'\r\n\r\n/** Roles with their expanded `bundles` and resolved `effective_permissions`. */\r\nexport function listRoles() {\r\n return request('/roles/fetch')\r\n}\r\nexport function createRole(body) {\r\n return request('/roles/create', { method: 'POST', body })\r\n}\r\nexport function updateRole(recordId, body) {\r\n return request('/roles/update', { method: 'PUT', params: { record_id: recordId }, body })\r\n}\r\nexport function deleteRole(recordId) {\r\n return request('/roles/delete', { method: 'DELETE', params: { record_id: recordId } })\r\n}\r\n\r\n/** Permission bundles (41 seeded), each resolving to a set of tag names. */\r\nexport function listPermissions() {\r\n return request('/permissions/fetch')\r\n}\r\nexport function createPermission(body) {\r\n return request('/permissions/create', { method: 'POST', body })\r\n}\r\nexport function updatePermission(recordId, body) {\r\n return request('/permissions/update', { method: 'PUT', params: { record_id: recordId }, body })\r\n}\r\n\r\n/** The 104-tag catalog: 13 modules x 8 actions. */\r\nexport function listPermissionTags() {\r\n return request('/permission-tags/fetch')\r\n}\r\n"],"names":["listRoles","request","createRole","body"],"mappings":"yCAGO,SAASA,GAAY,CAC1B,OAAOC,EAAQ,cAAc,CAC/B,CACO,SAASC,EAAWC,EAAM,CAC/B,OAAOF,EAAQ,gBAAiB,CAAE,OAAQ,OAAQ,KAAAE,CAAI,CAAE,CAC1D"}

View File

@ -0,0 +1,2 @@
var R=i=>{throw TypeError(i)};var E=(i,t,s)=>t.has(i)||R("Cannot "+s);var e=(i,t,s)=>(E(i,t,"read from private field"),s?s.call(i):t.get(i)),b=(i,t,s)=>t.has(i)?R("Cannot add the same private member more than once"):t instanceof WeakSet?t.add(i):t.set(i,s),p=(i,t,s,r)=>(E(i,t,"write to private field"),r?r.call(i,s):t.set(i,s),s),y=(i,t,s)=>(E(i,t,"access private method"),s);import{ao as q,ap as U,aq as j,ar as k,as as P,e as L,r as v,at as A,au as D}from"./index-BTPmxtwM.js";var a,c,o,h,n,C,S,w,I=(w=class extends q{constructor(t,s){super();b(this,n);b(this,a);b(this,c);b(this,o);b(this,h);p(this,a,t),this.setOptions(s),this.bindMethods(),y(this,n,C).call(this)}bindMethods(){this.mutate=this.mutate.bind(this),this.reset=this.reset.bind(this)}setOptions(t){var r;const s=this.options;this.options=e(this,a).defaultMutationOptions(t),U(this.options,s)||e(this,a).getMutationCache().notify({type:"observerOptionsUpdated",mutation:e(this,o),observer:this}),s!=null&&s.mutationKey&&this.options.mutationKey&&j(s.mutationKey)!==j(this.options.mutationKey)?this.reset():((r=e(this,o))==null?void 0:r.state.status)==="pending"&&e(this,o).setOptions(this.options)}onUnsubscribe(){var t;this.hasListeners()||(t=e(this,o))==null||t.removeObserver(this)}onMutationUpdate(t){y(this,n,C).call(this),y(this,n,S).call(this,t)}getCurrentResult(){return e(this,c)}reset(){var t;(t=e(this,o))==null||t.removeObserver(this),p(this,o,void 0),y(this,n,C).call(this),y(this,n,S).call(this)}mutate(t,s){var r;return p(this,h,s),(r=e(this,o))==null||r.removeObserver(this),p(this,o,e(this,a).getMutationCache().build(e(this,a),this.options)),e(this,o).addObserver(this),e(this,o).execute(t)}},a=new WeakMap,c=new WeakMap,o=new WeakMap,h=new WeakMap,n=new WeakSet,C=function(){var s;const t=((s=e(this,o))==null?void 0:s.state)??k();p(this,c,{...t,isPending:t.status==="pending",isSuccess:t.status==="success",isError:t.status==="error",isIdle:t.status==="idle",mutate:this.mutate,reset:this.reset})},S=function(t){P.batch(()=>{var s,r,u,f,d,O,x,K;if(e(this,h)&&this.hasListeners()){const m=e(this,c).variables,M=e(this,c).context,g={client:e(this,a),meta:this.options.meta,mutationKey:this.options.mutationKey};if((t==null?void 0:t.type)==="success"){try{(r=(s=e(this,h)).onSuccess)==null||r.call(s,t.data,m,M,g)}catch(l){Promise.reject(l)}try{(f=(u=e(this,h)).onSettled)==null||f.call(u,t.data,null,m,M,g)}catch(l){Promise.reject(l)}}else if((t==null?void 0:t.type)==="error"){try{(O=(d=e(this,h)).onError)==null||O.call(d,t.error,m,M,g)}catch(l){Promise.reject(l)}try{(K=(x=e(this,h)).onSettled)==null||K.call(x,void 0,t.error,m,M,g)}catch(l){Promise.reject(l)}}}this.listeners.forEach(m=>{m(e(this,c))})})},w);function z(i,t){const s=L(),[r]=v.useState(()=>new I(s,i));v.useEffect(()=>{r.setOptions(i)},[r,i]);const u=v.useSyncExternalStore(v.useCallback(d=>r.subscribe(P.batchCalls(d)),[r]),()=>r.getCurrentResult(),()=>r.getCurrentResult()),f=v.useCallback((d,O)=>{r.mutate(d,O).catch(A)},[r]);if(u.error&&D(r.options.throwOnError,[u.error]))throw u.error;return{...u,mutate:f,mutateAsync:u.mutate}}export{z as u};
//# sourceMappingURL=useMutation-CMO99S-s.js.map

File diff suppressed because one or more lines are too long

32
frontend/dist/index.html vendored Normal file
View File

@ -0,0 +1,32 @@
<!DOCTYPE html>
<html lang="en" data-theme="light">
<head>
<meta charset="UTF-8" />
<!-- viewport-fit=cover lets the layout reach under the iOS notch/home bar;
the safe-area insets in styles.css keep content clear of them.
No maximum-scale/user-scalable — pinch-zoom must stay available. -->
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
<meta name="color-scheme" content="light dark" />
<meta name="mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
<meta name="format-detection" content="telephone=no" />
<!-- Per-route titles are set at runtime by useRouteMeta. -->
<title>TalentFlow · Applicant Tracking System</title>
<meta name="theme-color" content="#004d43" media="(prefers-color-scheme: light)" />
<meta name="theme-color" content="#0e1d1f" media="(prefers-color-scheme: dark)" />
<!-- Utopia brand type: Belleza (main headings) + Inter as the metric-
compatible stand-in for Neue Montreal, which is a licensed face.
If Neue Montreal is installed locally it wins via the CSS stack. -->
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Belleza&family=Inter:wght@400;500;600;700;800&display=swap" />
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><rect width='100' height='100' rx='22' fill='%23004d43'/><g transform='translate(14 32) scale(0.72)'><path d='M100 3.65C97.86 20.99 91.89 43.03 79.48 55.33 76 58.77 71.84 61.46 66.96 62.14 50.4 64.46 41.84 47.5 29.07 42.7 21.85 39.98 14.5 42.02 9.66 47.95 6.54 51.78 4.49 56.35 2.97 61.13 2.41 61.64 0.97 61.66 0 61.31L0 0.13C1.05 0 2.27 0.02 3.09 0.28 14.9 15.86 26.77 30.82 40.15 45.28L60.79 24.7C67.38 18.22 74.41 12.74 82.59 8.51 88.11 5.83 93.64 3.93 100 3.65Z' fill='%23ceff71'/></g></svg>" />
<script type="module" crossorigin src="/assets/index-BTPmxtwM.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-CSn67wit.css">
</head>
<body>
<div id="root"></div>
</body>
</html>

Some files were not shown because too many files have changed in this diff Show More