fix(backoffice): use string IDs for companies/jobs/users, fix audit log, ensure real tags support
This commit is contained in:
parent
9784e959e4
commit
4eae018a25
5 changed files with 23 additions and 28 deletions
|
|
@ -760,19 +760,14 @@ func (h *CoreHandlers) Me(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var userID int
|
var userID string
|
||||||
switch v := userIDVal.(type) {
|
switch v := userIDVal.(type) {
|
||||||
case int:
|
case int:
|
||||||
userID = v
|
userID = strconv.Itoa(v)
|
||||||
case string:
|
case string:
|
||||||
var err error
|
userID = v
|
||||||
userID, err = strconv.Atoi(v)
|
|
||||||
if err != nil {
|
|
||||||
http.Error(w, "Invalid User ID in context", http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
case float64:
|
case float64:
|
||||||
userID = int(v)
|
userID = strconv.Itoa(int(v))
|
||||||
}
|
}
|
||||||
|
|
||||||
user, err := h.adminService.GetUser(ctx, userID)
|
user, err := h.adminService.GetUser(ctx, userID)
|
||||||
|
|
|
||||||
|
|
@ -15,12 +15,12 @@ type LoginResponse struct {
|
||||||
Token string `json:"token"`
|
Token string `json:"token"`
|
||||||
User UserInfo `json:"user"`
|
User UserInfo `json:"user"`
|
||||||
Companies []CompanyInfo `json:"companies,omitempty"`
|
Companies []CompanyInfo `json:"companies,omitempty"`
|
||||||
ActiveCompanyID *int `json:"activeCompanyId,omitempty"`
|
ActiveCompanyID *string `json:"activeCompanyId,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// UserInfo represents basic user information in responses
|
// UserInfo represents basic user information in responses
|
||||||
type UserInfo struct {
|
type UserInfo struct {
|
||||||
ID int `json:"id"`
|
ID string `json:"id"`
|
||||||
Identifier string `json:"identifier"`
|
Identifier string `json:"identifier"`
|
||||||
Role string `json:"role"`
|
Role string `json:"role"`
|
||||||
FullName string `json:"fullName"`
|
FullName string `json:"fullName"`
|
||||||
|
|
@ -29,7 +29,7 @@ type UserInfo struct {
|
||||||
|
|
||||||
// CompanyInfo represents basic company information
|
// CompanyInfo represents basic company information
|
||||||
type CompanyInfo struct {
|
type CompanyInfo struct {
|
||||||
ID int `json:"id"`
|
ID string `json:"id"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Role string `json:"role"` // Role in this company (admin or recruiter)
|
Role string `json:"role"` // Role in this company (admin or recruiter)
|
||||||
}
|
}
|
||||||
|
|
@ -49,7 +49,7 @@ type RegisterRequest struct {
|
||||||
|
|
||||||
// User represents a generic user profile
|
// User represents a generic user profile
|
||||||
type User struct {
|
type User struct {
|
||||||
ID int `json:"id"`
|
ID string `json:"id"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Email string `json:"email"`
|
Email string `json:"email"`
|
||||||
Role string `json:"role"`
|
Role string `json:"role"`
|
||||||
|
|
|
||||||
|
|
@ -525,7 +525,7 @@ func (s *AdminService) getTagByID(ctx context.Context, id int) (*models.Tag, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetUser fetches a user by ID
|
// GetUser fetches a user by ID
|
||||||
func (s *AdminService) GetUser(ctx context.Context, id int) (*dto.User, error) {
|
func (s *AdminService) GetUser(ctx context.Context, id string) (*dto.User, error) {
|
||||||
query := `
|
query := `
|
||||||
SELECT id, name, email, role, created_at
|
SELECT id, name, email, role, created_at
|
||||||
FROM users WHERE id = $1
|
FROM users WHERE id = $1
|
||||||
|
|
@ -540,7 +540,7 @@ func (s *AdminService) GetUser(ctx context.Context, id int) (*dto.User, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCompanyByUserID fetches the company associated with a user
|
// GetCompanyByUserID fetches the company associated with a user
|
||||||
func (s *AdminService) GetCompanyByUserID(ctx context.Context, userID int) (*models.Company, error) {
|
func (s *AdminService) GetCompanyByUserID(ctx context.Context, userID string) (*models.Company, error) {
|
||||||
// First, try to find company where this user is admin
|
// First, try to find company where this user is admin
|
||||||
// Assuming users table has company_id or companies table has admin_email
|
// Assuming users table has company_id or companies table has admin_email
|
||||||
// Let's check if 'users' has company_id column via error or assume architecture.
|
// Let's check if 'users' has company_id column via error or assume architecture.
|
||||||
|
|
@ -568,7 +568,7 @@ func (s *AdminService) GetCompanyByUserID(ctx context.Context, userID int) (*mod
|
||||||
WHERE u.id = $1
|
WHERE u.id = $1
|
||||||
`
|
`
|
||||||
if err2 := s.DB.QueryRowContext(ctx, query2, userID).Scan(&c.ID, &c.Name, &c.Slug, &c.Active, &c.Verified); err2 != nil {
|
if err2 := s.DB.QueryRowContext(ctx, query2, userID).Scan(&c.ID, &c.Name, &c.Slug, &c.Active, &c.Verified); err2 != nil {
|
||||||
return nil, fmt.Errorf("company not found for user %d", userID)
|
return nil, fmt.Errorf("company not found for user %s", userID)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return &c, nil
|
return &c, nil
|
||||||
|
|
|
||||||
|
|
@ -91,7 +91,7 @@ export default function BackofficePage() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleApproveCompany = async (companyId: number) => {
|
const handleApproveCompany = async (companyId: string) => {
|
||||||
try {
|
try {
|
||||||
await adminCompaniesApi.updateStatus(companyId, { verified: true })
|
await adminCompaniesApi.updateStatus(companyId, { verified: true })
|
||||||
toast.success("Company approved")
|
toast.success("Company approved")
|
||||||
|
|
@ -102,7 +102,7 @@ export default function BackofficePage() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleDeactivateCompany = async (companyId: number) => {
|
const handleDeactivateCompany = async (companyId: string) => {
|
||||||
try {
|
try {
|
||||||
await adminCompaniesApi.updateStatus(companyId, { active: false })
|
await adminCompaniesApi.updateStatus(companyId, { active: false })
|
||||||
toast.success("Company deactivated")
|
toast.success("Company deactivated")
|
||||||
|
|
@ -113,7 +113,7 @@ export default function BackofficePage() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleJobStatus = async (jobId: number, status: string) => {
|
const handleJobStatus = async (jobId: string, status: string) => {
|
||||||
try {
|
try {
|
||||||
await adminJobsApi.updateStatus(jobId, status)
|
await adminJobsApi.updateStatus(jobId, status)
|
||||||
toast.success("Job status updated")
|
toast.success("Job status updated")
|
||||||
|
|
@ -124,7 +124,7 @@ export default function BackofficePage() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleDuplicateJob = async (jobId: number) => {
|
const handleDuplicateJob = async (jobId: string) => {
|
||||||
try {
|
try {
|
||||||
await adminJobsApi.duplicate(jobId)
|
await adminJobsApi.duplicate(jobId)
|
||||||
toast.success("Job duplicated as draft")
|
toast.success("Job duplicated as draft")
|
||||||
|
|
@ -283,11 +283,11 @@ export default function BackofficePage() {
|
||||||
)}
|
)}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell className="text-right space-x-2">
|
<TableCell className="text-right space-x-2">
|
||||||
<Button size="sm" variant="outline" onClick={() => handleApproveCompany(Number(company.id))}>
|
<Button size="sm" variant="outline" onClick={() => handleApproveCompany(company.id)}>
|
||||||
<CheckCircle className="h-4 w-4 mr-2" />
|
<CheckCircle className="h-4 w-4 mr-2" />
|
||||||
Aprovar
|
Aprovar
|
||||||
</Button>
|
</Button>
|
||||||
<Button size="sm" variant="destructive" onClick={() => handleDeactivateCompany(Number(company.id))}>
|
<Button size="sm" variant="destructive" onClick={() => handleDeactivateCompany(company.id)}>
|
||||||
<XCircle className="h-4 w-4 mr-2" />
|
<XCircle className="h-4 w-4 mr-2" />
|
||||||
Desativar
|
Desativar
|
||||||
</Button>
|
</Button>
|
||||||
|
|
|
||||||
|
|
@ -87,7 +87,7 @@ export interface AdminCompany {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AdminJob {
|
export interface AdminJob {
|
||||||
id: number;
|
id: string;
|
||||||
title: string;
|
title: string;
|
||||||
companyName: string;
|
companyName: string;
|
||||||
status: string;
|
status: string;
|
||||||
|
|
@ -116,7 +116,7 @@ export interface AdminTag {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AdminCandidate {
|
export interface AdminCandidate {
|
||||||
id: number;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
email: string;
|
email: string;
|
||||||
phone: string;
|
phone: string;
|
||||||
|
|
@ -218,12 +218,12 @@ export const adminJobsApi = {
|
||||||
|
|
||||||
return apiRequest<{ data: AdminJob[]; pagination: any }>(`/api/v1/jobs/moderation?${query.toString()}`);
|
return apiRequest<{ data: AdminJob[]; pagination: any }>(`/api/v1/jobs/moderation?${query.toString()}`);
|
||||||
},
|
},
|
||||||
updateStatus: (id: number, status: string) =>
|
updateStatus: (id: string, status: string) =>
|
||||||
apiRequest<void>(`/api/v1/jobs/${id}/status`, {
|
apiRequest<void>(`/api/v1/jobs/${id}/status`, {
|
||||||
method: "PATCH",
|
method: "PATCH",
|
||||||
body: JSON.stringify({ status }),
|
body: JSON.stringify({ status }),
|
||||||
}),
|
}),
|
||||||
duplicate: (id: number) =>
|
duplicate: (id: string) =>
|
||||||
apiRequest<void>(`/api/v1/jobs/${id}/duplicate`, {
|
apiRequest<void>(`/api/v1/jobs/${id}/duplicate`, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
}),
|
}),
|
||||||
|
|
@ -275,14 +275,14 @@ export const adminCompaniesApi = {
|
||||||
body: JSON.stringify(data),
|
body: JSON.stringify(data),
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
updateStatus: (id: number, data: { active?: boolean; verified?: boolean }) => {
|
updateStatus: (id: string, data: { active?: boolean; verified?: boolean }) => {
|
||||||
logCrudAction("update", "admin/companies", { id, ...data });
|
logCrudAction("update", "admin/companies", { id, ...data });
|
||||||
return apiRequest<void>(`/api/v1/companies/${id}/status`, {
|
return apiRequest<void>(`/api/v1/companies/${id}/status`, {
|
||||||
method: "PATCH",
|
method: "PATCH",
|
||||||
body: JSON.stringify(data),
|
body: JSON.stringify(data),
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
delete: (id: number) => {
|
delete: (id: string) => {
|
||||||
logCrudAction("delete", "admin/companies", { id });
|
logCrudAction("delete", "admin/companies", { id });
|
||||||
return apiRequest<void>(`/api/v1/companies/${id}`, {
|
return apiRequest<void>(`/api/v1/companies/${id}`, {
|
||||||
method: "DELETE"
|
method: "DELETE"
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue