fix(admin): add and update

parent c5eb854f
...@@ -171,7 +171,10 @@ export class JobFormComponent implements OnInit { ...@@ -171,7 +171,10 @@ export class JobFormComponent implements OnInit {
isEditMode = false; isEditMode = false;
ngOnInit(): void { ngOnInit(): void {
console.log('JobFormComponent ngOnInit, job input:', this.job);
this.isEditMode = !!this.job; this.isEditMode = !!this.job;
console.log('Edit mode:', this.isEditMode);
this.initializeForm(); this.initializeForm();
if (this.job) { if (this.job) {
...@@ -193,7 +196,9 @@ export class JobFormComponent implements OnInit { ...@@ -193,7 +196,9 @@ export class JobFormComponent implements OnInit {
} }
private populateForm(job: Job): void { private populateForm(job: Job): void {
this.jobForm.patchValue({ console.log('Populating form with job data:', job);
const formData = {
title: job.title, title: job.title,
description: job.description, description: job.description,
company: job.company, company: job.company,
...@@ -202,17 +207,28 @@ export class JobFormComponent implements OnInit { ...@@ -202,17 +207,28 @@ export class JobFormComponent implements OnInit {
type: job.type, type: job.type,
how_to_apply: job.how_to_apply || '', how_to_apply: job.how_to_apply || '',
company_logo: job.company_logo || '' company_logo: job.company_logo || ''
}); };
console.log('Form data to patch:', formData);
this.jobForm.patchValue(formData);
console.log('Form value after patch:', this.jobForm.value);
} }
onSubmit(): void { onSubmit(): void {
console.log('Form submit called, valid:', this.jobForm.valid);
console.log('Current form value:', this.jobForm.value);
console.log('Edit mode:', this.isEditMode);
if (this.jobForm.valid) { if (this.jobForm.valid) {
const formValue = this.jobForm.value; const formValue = this.jobForm.value;
const jobData: JobFormData = { const jobData: JobFormData = {
...formValue ...formValue
}; };
console.log('Emitting job data:', jobData);
this.formSubmit.emit(jobData); this.formSubmit.emit(jobData);
} else {
console.log('Form is invalid, errors:', this.jobForm.errors);
} }
} }
......
...@@ -84,25 +84,25 @@ import { JobFormComponent } from '../../components/job-form/job-form.component'; ...@@ -84,25 +84,25 @@ import { JobFormComponent } from '../../components/job-form/job-form.component';
</thead> </thead>
<tbody> <tbody>
@for (job of jobsTable.data; track job.id; let i = $index) { @for (job of jobsTable.data; track job.id || $index; let i = $index) {
<tr> <tr>
<td> <td>
<span class="tw-font-medium tw-text-gray-900">{{ i + 1 }}</span> <span class="tw-font-medium tw-text-gray-900">{{ i + 1 }}</span>
</td> </td>
<td> <td>
<div class="tw-font-medium tw-text-gray-900">{{ job.title }}</div> <div class="tw-font-medium tw-text-gray-900">{{ job.title || 'Untitled' }}</div>
<div class="tw-text-sm tw-text-gray-500">{{ job.company }}</div> <div class="tw-text-sm tw-text-gray-500">{{ job.company || 'Unknown Company' }}</div>
</td> </td>
<td> <td>
<nz-tag [nzColor]="getJobTypeColor(job.type)"> <nz-tag [nzColor]="getJobTypeColor(job.type || 'Full Time')">
{{ job.type }} {{ job.type || 'Full Time' }}
</nz-tag> </nz-tag>
</td> </td>
<td> <td>
<span class="tw-text-gray-700">{{ job.location }}</span> <span class="tw-text-gray-700">{{ job.location || 'Not specified' }}</span>
</td> </td>
<td nzAlign="center"> <td nzAlign="center">
...@@ -379,32 +379,40 @@ export class DashboardComponent implements OnInit { ...@@ -379,32 +379,40 @@ export class DashboardComponent implements OnInit {
} }
onEditJobFromDetail(job: Job): void { onEditJobFromDetail(job: Job): void {
// Close detail modal and open edit modal
this.showDetailModal = false; this.showDetailModal = false;
this.isEditMode = true; this.isEditMode = true;
this.selectedJob = job; this.selectedJob = job;
this.showJobFormModal = true; this.showJobFormModal = true;
this.cdr.markForCheck();
} }
onJobFormSubmit(jobData: JobFormData): void { onJobFormSubmit(jobData: JobFormData): void {
this.jobFormLoading.set(true); this.jobFormLoading.set(true);
this.cdr.markForCheck();
if (this.isEditMode && this.selectedJob) { if (this.isEditMode && this.selectedJob) {
// Update job // Update job
console.log('Updating job:', this.selectedJob.id, 'with data:', jobData);
this.jobApiService.updateJob({ id: this.selectedJob.id, job: jobData }).subscribe({ this.jobApiService.updateJob({ id: this.selectedJob.id, job: jobData }).subscribe({
next: (response) => { next: (response) => {
console.log('Update job response:', response);
if (response.success) { if (response.success) {
this.jobStateService.updateJob(response.responseData.job); this.jobStateService.updateJob(response.responseData.job);
this.messageService.success(`Job "${response.responseData.job.title}" updated successfully`); this.messageService.success(`Job "${response.responseData.job.title}" updated successfully`);
this.closeJobFormModal(); this.closeJobFormModal();
this.cdr.markForCheck();
} else { } else {
this.messageService.error(`Failed to update job: ${response.message}`); this.messageService.error(`Failed to update job: ${response.message}`);
this.jobFormLoading.set(false); this.jobFormLoading.set(false);
this.cdr.markForCheck();
} }
}, },
error: (error) => { error: (error) => {
console.error('Update job error:', error);
this.messageService.error(`Failed to update job: ${error.message}`); this.messageService.error(`Failed to update job: ${error.message}`);
this.jobFormLoading.set(false); this.jobFormLoading.set(false);
this.cdr.markForCheck();
} }
}); });
} else { } else {
...@@ -412,17 +420,26 @@ export class DashboardComponent implements OnInit { ...@@ -412,17 +420,26 @@ export class DashboardComponent implements OnInit {
this.jobApiService.createJob({ job: jobData }).subscribe({ this.jobApiService.createJob({ job: jobData }).subscribe({
next: (response) => { next: (response) => {
if (response.success) { if (response.success) {
console.log('Job created successfully, response:', response);
this.jobStateService.addJob(response.responseData.job); this.jobStateService.addJob(response.responseData.job);
this.messageService.success(`Job "${response.responseData.job.title}" created successfully`); this.messageService.success(`Job "${response.responseData.job.title}" created successfully`);
this.closeJobFormModal(); this.closeJobFormModal();
this.cdr.markForCheck();
setTimeout(() => {
this.refreshJobs();
}, 500);
} else { } else {
this.messageService.error(`Failed to create job: ${response.message}`); this.messageService.error(`Failed to create job: ${response.message}`);
this.jobFormLoading.set(false); this.jobFormLoading.set(false);
this.cdr.markForCheck();
} }
}, },
error: (error) => { error: (error) => {
this.messageService.error(`Failed to create job: ${error.message}`); this.messageService.error(`Failed to create job: ${error.message}`);
this.jobFormLoading.set(false); this.jobFormLoading.set(false);
this.cdr.markForCheck();
} }
}); });
} }
......
...@@ -17,7 +17,6 @@ import { ...@@ -17,7 +17,6 @@ import {
import { environment } from '../../../../environments/environment'; import { environment } from '../../../../environments/environment';
import { StorageService } from './storage.service'; import { StorageService } from './storage.service';
// API response format that matches your data structure
interface ApiJobResponse { interface ApiJobResponse {
message: string; message: string;
responseData: { responseData: {
...@@ -42,7 +41,6 @@ export class JobApiService { ...@@ -42,7 +41,6 @@ export class JobApiService {
private readonly storageService = inject(StorageService); private readonly storageService = inject(StorageService);
private readonly API_URL = `${environment.API_DOMAIN}/jobs`; private readonly API_URL = `${environment.API_DOMAIN}/jobs`;
// HTTP Headers
private getHeaders = (): HttpHeaders => { private getHeaders = (): HttpHeaders => {
const headers: { [key: string]: string } = { 'Content-Type': 'application/json' }; const headers: { [key: string]: string } = { 'Content-Type': 'application/json' };
const token = this.storageService.getToken(); const token = this.storageService.getToken();
...@@ -73,23 +71,30 @@ export class JobApiService { ...@@ -73,23 +71,30 @@ export class JobApiService {
}; };
private convertApiJobToJob = (apiJob: ApiJob): Job => { private convertApiJobToJob = (apiJob: ApiJob): Job => {
return { console.log('Converting API job to internal format:', apiJob);
id: apiJob.id,
title: apiJob.title, const job: Job = {
description: apiJob.description, id: apiJob.id || '',
company: apiJob.company, title: apiJob.title || '',
company_url: apiJob.company_url, description: apiJob.description || '',
location: apiJob.location, company: apiJob.company || '',
company_url: apiJob.company_url || '',
location: apiJob.location || '',
type: this.mapApiTypeToJobType(apiJob.type), type: this.mapApiTypeToJobType(apiJob.type),
created_at: apiJob.created_at, created_at: apiJob.created_at || new Date().toISOString(),
how_to_apply: "", how_to_apply: "",
company_logo: null company_logo: null
}; };
console.log('Converted job:', job);
return job;
}; };
private mapApiTypeToJobType = (apiType: string): JobType => { private mapApiTypeToJobType = (apiType: string): JobType => {
if (apiType.toLowerCase().includes('part')) return 'Part Time'; if (!apiType) return 'Full Time';
if (apiType.toLowerCase().includes('remote')) return 'Remote'; const lowerType = apiType.toLowerCase();
if (lowerType.includes('part')) return 'Part Time';
if (lowerType.includes('remote')) return 'Remote';
return 'Full Time'; return 'Full Time';
}; };
...@@ -122,6 +127,8 @@ export class JobApiService { ...@@ -122,6 +127,8 @@ export class JobApiService {
createJob = (request: CreateJobRequest): Observable<JobResponse> => { createJob = (request: CreateJobRequest): Observable<JobResponse> => {
console.log('Creating job with request:', request);
const apiJobData = { const apiJobData = {
title: request.job.title, title: request.job.title,
description: request.job.description, description: request.job.description,
...@@ -131,22 +138,34 @@ export class JobApiService { ...@@ -131,22 +138,34 @@ export class JobApiService {
type: request.job.type type: request.job.type
}; };
console.log('Sending API data:', apiJobData);
return this.http.post<ApiJob>(this.API_URL, apiJobData, { return this.http.post<ApiJob>(this.API_URL, apiJobData, {
headers: this.getHeaders() headers: this.getHeaders()
}).pipe( }).pipe(
map(apiJob => ({ map(apiJob => {
console.log('Received API response:', apiJob);
const convertedJob = this.convertApiJobToJob(apiJob);
const result = {
message: 'Job created successfully', message: 'Job created successfully',
responseData: { responseData: {
job: this.convertApiJobToJob(apiJob) job: convertedJob
}, },
success: true, success: true,
status: 201 status: 201
})), };
console.log('Final response:', result);
return result;
}),
catchError(this.handleError) catchError(this.handleError)
); );
}; };
updateJob = (request: UpdateJobRequest): Observable<JobResponse> => { updateJob = (request: UpdateJobRequest): Observable<JobResponse> => {
console.log('Updating job with request:', request);
// Convert internal job format to API format // Convert internal job format to API format
const apiJobData = { const apiJobData = {
title: request.job.title, title: request.job.title,
...@@ -157,17 +176,28 @@ export class JobApiService { ...@@ -157,17 +176,28 @@ export class JobApiService {
type: request.job.type type: request.job.type
}; };
console.log('Sending update API data:', apiJobData);
console.log('Update URL:', `${this.API_URL}/${request.id}`);
return this.http.put<ApiJob>(`${this.API_URL}/${request.id}`, apiJobData, { return this.http.put<ApiJob>(`${this.API_URL}/${request.id}`, apiJobData, {
headers: this.getHeaders() headers: this.getHeaders()
}).pipe( }).pipe(
map(apiJob => ({ map(apiJob => {
console.log('Received update API response:', apiJob);
const convertedJob = this.convertApiJobToJob(apiJob);
const result = {
message: 'Job updated successfully', message: 'Job updated successfully',
responseData: { responseData: {
job: this.convertApiJobToJob(apiJob) job: convertedJob
}, },
success: true, success: true,
status: 200 status: 200
})), };
console.log('Final update response:', result);
return result;
}),
catchError(this.handleError) catchError(this.handleError)
); );
}; };
......
...@@ -12,17 +12,14 @@ export class JobStateService { ...@@ -12,17 +12,14 @@ export class JobStateService {
private errorSignal = signal<string | null>(null); private errorSignal = signal<string | null>(null);
private statsSignal = signal<JobStats | null>(null); private statsSignal = signal<JobStats | null>(null);
// Public readonly signals
jobs = this.jobsSignal.asReadonly(); jobs = this.jobsSignal.asReadonly();
loading = this.loadingSignal.asReadonly(); loading = this.loadingSignal.asReadonly();
error = this.errorSignal.asReadonly(); error = this.errorSignal.asReadonly();
stats = this.statsSignal.asReadonly(); stats = this.statsSignal.asReadonly();
// Computed properties
totalJobs = computed(() => this.stats()?.totalJobs || this.jobs().length); totalJobs = computed(() => this.stats()?.totalJobs || this.jobs().length);
companiesCount = computed(() => this.stats()?.companiesCount || new Set(this.jobs().map(job => job.company)).size); companiesCount = computed(() => this.stats()?.companiesCount || new Set(this.jobs().map(job => job.company)).size);
// Actions
loadJobs = (): Observable<Job[]> => { loadJobs = (): Observable<Job[]> => {
this.setLoading(true); this.setLoading(true);
this.clearError(); this.clearError();
...@@ -64,18 +61,34 @@ export class JobStateService { ...@@ -64,18 +61,34 @@ export class JobStateService {
}; };
addJob = (job: Job): void => { addJob = (job: Job): void => {
console.log('Adding new job to state:', job);
const currentJobs = this.jobsSignal(); const currentJobs = this.jobsSignal();
this.setJobs([job, ...currentJobs]); console.log('Current jobs before adding:', currentJobs.length);
const updatedJobs = [job, ...currentJobs];
this.setJobs(updatedJobs);
this.updateStats(); this.updateStats();
console.log('Jobs after adding:', this.jobsSignal().length);
}; };
updateJob = (updatedJob: Job): void => { updateJob = (updatedJob: Job): void => {
console.log('Updating job in state:', updatedJob);
const currentJobs = this.jobsSignal(); const currentJobs = this.jobsSignal();
const updatedJobs = currentJobs.map(job => console.log('Current jobs before update:', currentJobs.length);
job.id === updatedJob.id ? updatedJob : job
); const updatedJobs = currentJobs.map(job => {
if (job.id === updatedJob.id) {
console.log('Found job to update:', job.id);
return updatedJob;
}
return job;
});
this.setJobs(updatedJobs); this.setJobs(updatedJobs);
this.updateStats(); this.updateStats();
console.log('Jobs after update:', this.jobsSignal().length);
}; };
removeJob = (jobId: string): void => { removeJob = (jobId: string): void => {
......
...@@ -13,18 +13,15 @@ export class JobService { ...@@ -13,18 +13,15 @@ export class JobService {
private readonly jobApiService = inject(JobApiService); private readonly jobApiService = inject(JobApiService);
private readonly jobStateService = inject(JobStateService); private readonly jobStateService = inject(JobStateService);
// Expose state
jobs = this.jobStateService.jobs; jobs = this.jobStateService.jobs;
loading = this.jobStateService.loading; loading = this.jobStateService.loading;
error = this.jobStateService.error; error = this.jobStateService.error;
stats = this.jobStateService.stats; stats = this.jobStateService.stats;
// Computed stats
totalJobs = this.jobStateService.totalJobs; totalJobs = this.jobStateService.totalJobs;
companiesCount = this.jobStateService.companiesCount; companiesCount = this.jobStateService.companiesCount;
constructor() { constructor() {
// Load jobs from API on service initialization
this.loadJobs().subscribe({ this.loadJobs().subscribe({
next: () => { next: () => {
console.log('Initial jobs loaded from API'); console.log('Initial jobs loaded from API');
...@@ -35,14 +32,12 @@ export class JobService { ...@@ -35,14 +32,12 @@ export class JobService {
}); });
} }
// Main API methods
loadJobs = (): Observable<Job[]> => loadJobs = (): Observable<Job[]> =>
this.jobStateService.loadJobs(); this.jobStateService.loadJobs();
loadJobStats = (): Observable<JobStats | null> => loadJobStats = (): Observable<JobStats | null> =>
this.jobStateService.loadJobStats(); this.jobStateService.loadJobStats();
// Helper methods for job data formatting
formatJobData = (formData: JobFormData): JobFormData => { formatJobData = (formData: JobFormData): JobFormData => {
return { return {
...formData, ...formData,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment