fix(admin): add and update

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