diff --git a/backend/job/app.py b/backend/job/app.py new file mode 100644 index 0000000..c5f1577 --- /dev/null +++ b/backend/job/app.py @@ -0,0 +1,49 @@ +import profile +from fastapi import APIRouter,Depends, Query +from fastapi.responses import JSONResponse +from fastapi import HTTPException +from db_setup import get_session +from sqlalchemy.ext.asyncio import AsyncSession +from pydantic import BaseModel, EmailStr, model_validator +from users.views import User +from users.permissions import CurrentUser, PermissionTag, require_permission +from job_post.views import JobPost + + +from dotenv import load_dotenv +load_dotenv() + +router = APIRouter() + +@router.post("/candidate/cv_upload") +async def cv_upload(session: AsyncSession = Depends(get_session),current_user: Depends(require_permission(PermissionTag.CANDIDATES_MANAGE,PermissionTag.CANDIDATES_VIEW,PermissionTag.CANDIDATES_EDIT,PermissionTag.CANDIDATES_DELETE,PermissionTag.CANDIDATES_CREATE))): + try: + pass + except HTTPException: + raise + except Exception as e: + raise HTTPException(status_code=500,detail=str(e)) + + +@router.post("/job/post-job") +async def post_job( + session: AsyncSession = Depends(get_session), + current_user: Depends(require_permission( + PermissionTag.JOB_BOARD_CREATE, + PermissionTag.JOB_BOARD_VIEW, + PermissionTag.JOB_BOARD_APPROVE, + PermissionTag.JOB_BOARD_EDIT, + PermissionTag.JOB_BOARD_DELETE, + PermissionTag.JOB_BOARD_VIEW_ALL, + PermissionTag.JOB_BOARD_APPROVE_ALL, + PermissionTag.JOB_BOARD_EDIT_ALL, + PermissionTag.JOB_BOARD_DELETE_ALL, + PermissionTag.JOB_BOARD_VIEW_ALL, + PermissionTag.JOB_BOARD_APPROVE_ALL, + PermissionTag.JOB_BOARD_EDIT_ALL, + PermissionTag.JOB_BOARD_DELETE_ALL, + ))): + try: + service=JobPost(session=session) + except Exception as e: + raise HTTPException(status_code=500,detail=str(e)) diff --git a/backend/job/job_post/models.py b/backend/job/job_post/models.py new file mode 100644 index 0000000..9f1719c --- /dev/null +++ b/backend/job/job_post/models.py @@ -0,0 +1,25 @@ +from os import setegid +import uuid +from datetime import datetime + +from sqlalchemy import func, or_ +from sqlalchemy.ext.asyncio import AsyncSession +from sqlalchemy.orm import selectinload +from sqlalchemy.orm.base import state_str +from sqlmodel import Field, Relationship, SQLModel, select + +class JobPosts(SQLModel, table=True): + __tablename__ = "job_posts" + id: uuid.UUID = Field(default_factory=uuid.uuid4, primary_key=True) + title: str + position:str + team:str + location:str + requirements:str + responsibilities:str + benefits:str + salary_range:str + description: str + created_at: datetime = Field(default_factory=datetime.now) + updated_at: datetime = Field(default_factory=datetime.now) + is_deleted: bool = Field(default=False) diff --git a/backend/job/job_post/views.py b/backend/job/job_post/views.py new file mode 100644 index 0000000..0aa0cfd --- /dev/null +++ b/backend/job/job_post/views.py @@ -0,0 +1,17 @@ +import os +from dotenv import load_dotenv +load_dotenv() +from sqlalchemy.ext.asyncio import AsyncSession + + +class JobPost: + def __init__(self,session:AsyncSession): + self.session=session + self.buffer_api=os.getenv("BUFFER_API") + self.client_id=os.getenv("CLIENT_ID") + + async def post_job(self,payload): + try: + pass + except Exception as e: + raise HTTPException(status_code=500,detail=str(e)) \ No newline at end of file diff --git a/backend/main.py b/backend/main.py index 2f3905b..2c07775 100644 --- a/backend/main.py +++ b/backend/main.py @@ -9,6 +9,7 @@ from inbox.app import router as inbox_router from users.app import router as users_router from role.app import router as role_router from forget_password.app import router as forget_password_router +from job.app import router as candidate_router from notifications.app import router as confirmation_router # Without this the db/migration logs have no handler and are swallowed under uvicorn. logging.basicConfig(level=logging.INFO, format="%(levelname)-8s %(name)s: %(message)s") @@ -28,4 +29,5 @@ app.include_router(inbox_router) app.include_router(users_router) app.include_router(role_router) app.include_router(forget_password_router) -app.include_router(confirmation_router) \ No newline at end of file +app.include_router(confirmation_router) +app.include_router(candidate_router) \ No newline at end of file diff --git a/frontend/.env.development b/frontend/.env.development index 27786df..de07be0 100644 --- a/frontend/.env.development +++ b/frontend/.env.development @@ -1 +1,3 @@ -VITE_API_BASE=http://localhost:8000 +# Use 127.0.0.1, not localhost. On this machine localhost prefers ::1 and hits a +# different listener (WSL/Docker on :8000) instead of the Windows uvicorn on 127.0.0.1. +VITE_API_BASE=http://127.0.0.1:8000 diff --git a/frontend/src/screens/Settings.jsx b/frontend/src/screens/Settings.jsx index 97bdffb..df39e41 100644 --- a/frontend/src/screens/Settings.jsx +++ b/frontend/src/screens/Settings.jsx @@ -2,10 +2,11 @@ Settings — 10 tabs. Two are real, eight are inert chrome exactly as in the prototype. - The Users tab is wired to GET /users/fetch, and Appearance drives the real - ThemeProvider. Everything else (General, Roles, Permissions, Notifications, - Email Templates, Career Portal, Branding, Security) is markup with no - persistence — same as the prototype. + The Users tab is wired to GET /users/fetch and its row pencil assigns roles + through PUT /users/assign-role / PUT /users/remove-role; Appearance drives the + real ThemeProvider. Everything else (General, Roles, Permissions, + Notifications, Email Templates, Career Portal, Branding, Security) is markup + with no persistence — same as the prototype. The Security tab in particular renders 2FA and audit logging as ENABLED while enforcing nothing; 01-repository-assessment.md §2.4 calls that out as @@ -14,14 +15,18 @@ ============================================================ */ import { useState } from 'react' -import { useQuery } from '@tanstack/react-query' +import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query' +import Modal from '../ui/Modal' import { Tabs } from '../ui/Tabs' -import { Avatar, Badge, Icon } from '../ui/primitives' +import { Avatar, Badge, FieldError, Icon } from '../ui/primitives' import { useToast } from '../ui/Toast' import { useTheme } from '../theme/ThemeProvider' +import { useFormState } from '../components/AuthLayout' +import { usePermission } from '../auth/AuthContext' import { qk } from '../lib/queryKeys' import { friendlyAuthError } from '../lib/errors' +import * as rolesApi from '../api/roles' import * as usersApi from '../api/users' import { roles as seedRoles } from '../data/seed' @@ -122,6 +127,7 @@ function General() { /** Real data: GET /users/fetch (requires rbac_users.view). */ function Users() { const { toast } = useToast() + const [editing, setEditing] = useState(null) const usersQuery = useQuery({ queryKey: qk.users.list(), queryFn: () => usersApi.list({ top: 50 }).then((r) => r.data ?? []), @@ -180,7 +186,11 @@ function Users() {