Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
M
Meu-Template-Angular-CSR
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Trần Anh Phú
Meu-Template-Angular-CSR
Commits
5465e871
Commit
5465e871
authored
Dec 23, 2024
by
tinhbe
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
update
parent
d3f8c729
Changes
33
Hide whitespace changes
Inline
Side-by-side
Showing
33 changed files
with
922 additions
and
420 deletions
+922
-420
JobDetail.component.html
.../Management/Job/Action/JobDetail/JobDetail.component.html
+57
-0
JobDetail.component.ts
...in/Management/Job/Action/JobDetail/JobDetail.component.ts
+41
-0
Modal.component.html
...admin/Management/Job/Action/ModalJob/Modal.component.html
+97
-0
Modal.component.ts
.../+admin/Management/Job/Action/ModalJob/Modal.component.ts
+150
-0
Job.component.html
src/app/+admin/Management/Job/Job.component.html
+34
-57
Job.component.scss
src/app/+admin/Management/Job/Job.component.scss
+0
-0
Job.component.ts
src/app/+admin/Management/Job/Job.component.ts
+109
-136
jobModel.model.ts
src/app/+admin/data-access/model/jobModel.model.ts
+22
-11
JobService.service.ts
src/app/+admin/data-access/services/JobService.service.ts
+11
-24
footer.component.html
...layout/feature/ui/Components/footer/footer.component.html
+8
-12
footer.component.ts
...n/layout/feature/ui/Components/footer/footer.component.ts
+7
-0
header.component.html
...layout/feature/ui/Components/header/header.component.html
+0
-4
header.component.ts
...n/layout/feature/ui/Components/header/header.component.ts
+0
-14
layout.component.html
src/app/+admin/layout/feature/ui/layout.component.html
+49
-20
layout.component.scss
src/app/+admin/layout/feature/ui/layout.component.scss
+3
-1
layout.component.ts
src/app/+admin/layout/feature/ui/layout.component.ts
+6
-4
JobCard.component.html
src/app/+home/component/JobCard/JobCard.component.html
+0
-33
JobCard.component.ts
src/app/+home/component/JobCard/JobCard.component.ts
+0
-29
JobDetail.component.html
src/app/+home/component/JobDetail/JobDetail.component.html
+39
-0
JobDetail.component.ts
src/app/+home/component/JobDetail/JobDetail.component.ts
+36
-0
JobList.component.html
src/app/+home/component/JobList/JobList.component.html
+59
-0
JobList.component.ts
src/app/+home/component/JobList/JobList.component.ts
+100
-0
JobListCard.component.html
...pp/+home/component/JobListCard/JobListCard.component.html
+0
-13
JobListCard.component.ts
src/app/+home/component/JobListCard/JobListCard.component.ts
+0
-40
Job.service.ts
src/app/+home/data-access/service/Job.service.ts
+5
-0
Auth.Service.ts
src/app/+login/data-access/Services/Auth.Service.ts
+0
-2
login.component.html
src/app/+login/feature/login.component.html
+6
-6
login.component.ts
src/app/+login/feature/login.component.ts
+2
-5
shell.routes.ts
src/app/+shell/feature/shell.routes.ts
+7
-1
header.component.html
...+shell/ui/components/header/feature/header.component.html
+7
-6
header.component.ts
...p/+shell/ui/components/header/feature/header.component.ts
+6
-0
layout.component.ts
src/app/+shell/ui/layout.component.ts
+4
-2
app.component.ts
src/app/app.component.ts
+57
-0
No files found.
src/app/+admin/Management/Job/Action/JobDetail/JobDetail.component.html
0 → 100644
View file @
5465e871
<nz-modal
[(
nzVisible
)]="
showViewModalComponent
"
nzTitle=
"Chi tiết công việc"
(
nzOnCancel
)="
handleCancel
()"
(
nzOnOk
)="
handleOk
()"
>
<ng-content
*
nzModalContent
>
<div
*
ngIf=
"jobData"
>
<div
class=
"tw-flex tw-flex-col tw-space-y-4"
>
<div
*
ngIf=
"jobData.company_logo"
>
<img
[
src
]="
jobData
.
company_logo
"
alt=
"{{ jobData.company }}"
class=
"tw-w-32 tw-h-32 tw-object-contain"
/>
</div>
<div><strong>
Loại công việc:
</strong>
{{ jobData.type || "N/A" }}
</div>
<div><strong>
Tiêu đề:
</strong>
{{ jobData.title || "N/A" }}
</div>
<div>
<strong>
Công ty:
</strong>
<a
*
ngIf=
"jobData.company_url"
[
href
]="
jobData
.
company_url
"
target=
"_blank"
rel=
"noopener noreferrer"
>
{{ jobData.company || "N/A" }}
</a>
<span
*
ngIf=
"!jobData.company_url"
>
{{ jobData.company || "N/A" }}
</span>
</div>
<div><strong>
Địa chỉ:
</strong>
{{ jobData.location || "N/A" }}
</div>
<div>
<strong>
Mô tả:
</strong>
<div
[
innerHTML
]="
getSanitizedDescription
(
jobData
.
description
||
'
N
/
A
')"
></div>
</div>
<div>
<strong>
Cách thức ứng tuyển:
</strong>
<div
[
innerHTML
]="
getSanitizedHowToApply
(
jobData
.
how_to_apply
||
'
N
/
A
')"
></div>
</div>
<div>
<strong>
Ngày tạo:
</strong>
{{ jobData.created_at | date : "short" }}
</div>
</div>
</div>
</ng-content>
</nz-modal>
src/app/+admin/Management/Job/Action/JobDetail/JobDetail.component.ts
0 → 100644
View file @
5465e871
import
{
Component
,
Input
,
Output
,
EventEmitter
}
from
'@angular/core'
;
import
{
DomSanitizer
,
SafeHtml
}
from
'@angular/platform-browser'
;
import
{
NzModalModule
}
from
'ng-zorro-antd/modal'
;
import
{
NzButtonModule
}
from
'ng-zorro-antd/button'
;
import
{
CommonModule
,
DatePipe
}
from
'@angular/common'
;
import
{
JobModel
}
from
'../../../../data-access/model/jobModel.model'
;
@
Component
({
selector
:
'app-view-job'
,
templateUrl
:
'./JobDetail.component.html'
,
standalone
:
true
,
imports
:
[
NzModalModule
,
NzButtonModule
,
DatePipe
,
CommonModule
]
})
export
class
ViewJobComponent
{
@
Input
()
showViewModalComponent
:
boolean
=
false
;
@
Output
()
close
=
new
EventEmitter
<
void
>
();
@
Input
()
jobData
:
JobModel
.
Job
|
null
=
null
;
constructor
(
private
sanitizer
:
DomSanitizer
)
{}
handleOk
():
void
{
this
.
close
.
emit
();
}
handleCancel
():
void
{
this
.
close
.
emit
();
}
getSanitizedDescription
(
description
:
string
|
null
|
undefined
):
SafeHtml
{
return
description
?
this
.
sanitizer
.
bypassSecurityTrustHtml
(
description
)
:
'N/A'
;
}
getSanitizedHowToApply
(
how_to_apply
:
string
|
null
|
undefined
):
SafeHtml
{
return
how_to_apply
?
this
.
sanitizer
.
bypassSecurityTrustHtml
(
how_to_apply
)
:
'N/A'
;
}
}
src/app/+admin/Management/Job/Action/ModalJob/Modal.component.html
0 → 100644
View file @
5465e871
<nz-modal
[(
nzVisible
)]="
showModal
"
[
nzTitle
]="(
isEdit
)?
'
Chi
ti
ế
t
c
ô
ng
vi
ệ
c
'
:
'
Th
ê
m
m
ớ
i
c
ô
ng
vi
ệ
c
'"
(
nzOnCancel
)="
onCancel
()"
(
nzOnOk
)="
onSubmit
()"
[
nzMaskClosable
]="
false
"
>
<ng-content
*
nzModalContent
>
<form
nz-form
[
formGroup
]="
JobForm
"
class=
"tw-p-6 tw-bg-white tw-rounded-lg tw-shadow-md tw-space-y-6"
>
<nz-form-item>
<nz-form-label
[
nzSpan
]="
7
"
>
Loại công việc
</nz-form-label>
<nz-form-control
[
nzSpan
]="
17
"
[
nzErrorTip
]="
getErrorTip
('
type
')"
>
<nz-select
formControlName=
"type"
placeholder=
"Loại công việc"
>
<nz-option
nzValue=
"Full Time"
nzLabel=
"Full Time"
></nz-option>
<nz-option
nzValue=
"Part Time"
nzLabel=
"Part Time"
></nz-option>
<nz-option
nzValue=
"Freelance"
nzLabel=
"Freelance"
></nz-option>
</nz-select>
</nz-form-control>
</nz-form-item>
<nz-form-item>
<nz-form-label
[
nzSpan
]="
7
"
>
Tiêu đề
</nz-form-label>
<nz-form-control
[
nzSpan
]="
17
"
[
nzErrorTip
]="
getErrorTip
('
title
')"
>
<input
nz-input
formControlName=
"title"
placeholder=
"Tiêu đề"
class=
"tw-w-full tw-p-2 tw-bg-white tw-border tw-rounded-md focus:tw-outline-none focus:tw-ring-1 focus:tw-ring-blue-500 focus:tw-border-blue-500"
/>
</nz-form-control>
</nz-form-item>
<nz-form-item>
<nz-form-label
[
nzSpan
]="
7
"
>
Công ty
</nz-form-label>
<nz-form-control
[
nzSpan
]="
17
"
[
nzErrorTip
]="
getErrorTip
('
company
')"
>
<input
nz-input
formControlName=
"company"
placeholder=
"Công ty"
class=
"tw-w-full tw-p-2 tw-bg-white tw-border tw-rounded-md focus:tw-outline-none focus:tw-ring-1 focus:tw-ring-blue-500 focus:tw-border-blue-500"
/>
</nz-form-control>
</nz-form-item>
<nz-form-item>
<nz-form-label
[
nzSpan
]="
7
"
>
Địa chỉ
</nz-form-label>
<nz-form-control
[
nzSpan
]="
17
"
[
nzErrorTip
]="
getErrorTip
('
location
')"
>
<input
nz-input
formControlName=
"location"
placeholder=
"Địa chỉ"
class=
"tw-w-full tw-p-2 tw-bg-white tw-border tw-rounded-md focus:tw-outline-none focus:tw-ring-1 focus:tw-ring-blue-500 focus:tw-border-blue-500"
/>
</nz-form-control>
</nz-form-item>
<nz-form-item>
<nz-form-label
[
nzSpan
]="
7
"
>
Mô tả
</nz-form-label>
<nz-form-control
[
nzSpan
]="
17
"
[
nzErrorTip
]="
getErrorTip
('
description
')"
>
<textarea
nz-input
formControlName=
"description"
placeholder=
"Mô tả công việc"
class=
"tw-w-full tw-h-40 tw-p-2 tw-bg-white tw-border tw-rounded-md focus:tw-outline-none focus:tw-ring-1 focus:tw-ring-blue-500 focus:tw-border-blue-500"
></textarea>
</nz-form-control>
</nz-form-item>
</form>
</ng-content>
</nz-modal>
src/app/+admin/Management/Job/Action/ModalJob/Modal.component.ts
0 → 100644
View file @
5465e871
import
{
ChangeDetectionStrategy
,
Component
,
EventEmitter
,
Input
,
OnChanges
,
Output
,
SimpleChanges
}
from
'@angular/core'
;
import
{
FormBuilder
,
FormGroup
,
Validators
}
from
'@angular/forms'
;
import
{
NzModalModule
}
from
'ng-zorro-antd/modal'
;
import
{
NzFormModule
}
from
'ng-zorro-antd/form'
;
import
{
NzInputModule
}
from
'ng-zorro-antd/input'
;
import
{
NzSelectModule
}
from
'ng-zorro-antd/select'
;
import
{
NzButtonModule
}
from
'ng-zorro-antd/button'
;
import
{
ReactiveFormsModule
}
from
'@angular/forms'
;
import
{
NzMessageService
}
from
'ng-zorro-antd/message'
;
import
{
CommonModule
}
from
'@angular/common'
;
import
{
JobModel
}
from
'../../../../data-access/model/jobModel.model'
;
import
{
JobService
}
from
'../../../../data-access/services/JobService.service'
;
import
{
catchError
,
of
,
tap
}
from
'rxjs'
;
@
Component
({
selector
:
'app-modal-job'
,
templateUrl
:
`Modal.component.html`
,
standalone
:
true
,
imports
:
[
NzModalModule
,
NzFormModule
,
NzInputModule
,
NzSelectModule
,
NzButtonModule
,
ReactiveFormsModule
,
CommonModule
],
changeDetection
:
ChangeDetectionStrategy
.
OnPush
,
})
export
class
ModalJobComponent
implements
OnChanges
{
@
Input
()
isEdit
:
boolean
=
false
;
@
Input
()
showModal
:
boolean
=
false
;
@
Input
()
jobData
:
JobModel
.
Job
|
null
=
null
;
@
Output
()
submit
=
new
EventEmitter
<
void
>
();
@
Output
()
close
=
new
EventEmitter
<
void
>
();
JobForm
:
FormGroup
;
isSubmitting
:
boolean
=
false
;
constructor
(
private
fb
:
FormBuilder
,
private
jobService
:
JobService
,
private
message
:
NzMessageService
)
{
this
.
JobForm
=
this
.
fb
.
group
({
type
:
[
null
,
[
Validators
.
required
]],
title
:
[
null
,
[
Validators
.
required
]],
company
:
[
null
,
[
Validators
.
required
]],
location
:
[
null
],
description
:
[
null
,
[
Validators
.
required
]],
how_to_apply
:
[
null
],
company_logo
:
[
null
],
});
}
ngOnChanges
(
changes
:
SimpleChanges
):
void
{
if
(
changes
[
'jobData'
]
&&
this
.
isEdit
&&
this
.
jobData
)
{
this
.
JobForm
.
patchValue
({
type
:
this
.
jobData
.
type
,
title
:
this
.
jobData
.
title
,
company
:
this
.
jobData
.
company
,
location
:
this
.
jobData
.
location
,
description
:
this
.
jobData
.
description
,
how_to_apply
:
this
.
jobData
.
how_to_apply
,
company_logo
:
this
.
jobData
.
company_logo
,
});
}
if
(
changes
[
'showModal'
]
&&
!
this
.
showModal
)
{
this
.
JobForm
.
reset
();
}
}
getErrorTip
(
controlName
:
string
):
string
{
const
control
=
this
.
JobForm
.
get
(
controlName
);
return
control
?.
errors
?
'vui lòng nhập trường '
+
controlName
:
''
;
}
onSubmit
():
void
{
this
.
isEdit
?
this
.
onSubmitEditJob
()
:
this
.
onSubmitAddJob
();
}
onSubmitEditJob
():
void
{
if
(
this
.
JobForm
.
valid
&&
this
.
jobData
)
{
this
.
isSubmitting
=
true
;
const
jobToUpdate
:
JobModel
.
Job
=
{
...
this
.
jobData
,
...
this
.
JobForm
.
value
,
};
this
.
jobService
.
editJob
(
jobToUpdate
).
pipe
(
tap
((
res
)
=>
{
this
.
isSubmitting
=
false
;
this
.
message
.
success
(
res
.
message
||
'Cập nhật công việc thành công!'
,
{
nzDuration
:
2500
,
});
this
.
submit
.
emit
();
this
.
onCancel
();
}),
catchError
((
err
)
=>
{
this
.
isSubmitting
=
false
;
this
.
message
.
error
(
err
.
message
||
'Đã xảy ra lỗi khi cập nhật công việc.'
,
{
nzDuration
:
2500
}
);
return
of
(
null
);
})
)
.
subscribe
();
}
}
onSubmitAddJob
():
void
{
if
(
this
.
JobForm
.
invalid
)
{
Object
.
values
(
this
.
JobForm
.
controls
).
forEach
(
control
=>
{
if
(
control
.
invalid
)
{
control
.
markAsDirty
();
control
.
updateValueAndValidity
();
}
});
return
;
}
const
newJob
:
JobModel
.
JobRequest
=
this
.
JobForm
.
value
;
console
.
log
(
newJob
);
this
.
jobService
.
addingNewJob
(
newJob
)
.
pipe
(
tap
((
res
)
=>
{
this
.
message
.
success
(
res
.
message
,
{
nzDuration
:
2500
});
this
.
submit
.
emit
();
this
.
JobForm
.
reset
();
this
.
showModal
=
false
;
}),
catchError
((
err
)
=>
{
this
.
message
.
error
(
err
.
message
,
{
nzDuration
:
2500
});
return
of
(
null
);
})
)
.
subscribe
();
}
onCancel
():
void
{
this
.
close
.
emit
();
this
.
JobForm
.
reset
();
}
}
src/app/+admin/Management/Job/Job.component.html
View file @
5465e871
<div
className=
"tw-container tw-content-center"
>
<div
class=
"tw-container tw-content-center"
>
<div
class=
"tw-flex tw-justify-end tw-pb-5"
>
<div
class=
"tw-flex tw-justify-end tw-pb-5 tw-pt-5"
>
<button
nz-button
nzType=
"primary"
(
click
)="
addJob
()"
>
+ Thêm công việc mới
</button>
<button
nz-button
nzType=
"primary"
(
click
)="
addJob
()"
>
+ Thêm công việc mới
</button>
</div>
</div>
<nz-table
<nz-table
#
JobTable
#
JobTable
[
nzData
]="
listOfJob
"
[
nzData
]="
listOfJob
"
[
nzLoading
]="
loading
"
[
nzFrontPagination
]="
true
"
[
nzFrontPagination
]="
true
"
nzShowSizeChanger
nzShowSizeChanger
[
nzPageSize
]="
5
"
[
nzPageSize
]="
5
"
[
nzScroll
]="{
x:
'
300px
'
}"
>
>
<thead>
<thead>
<tr>
<tr>
...
@@ -21,16 +23,21 @@
...
@@ -21,16 +23,21 @@
</tr>
</tr>
</thead>
</thead>
<tbody>
<tbody>
@for (data of JobTable.data; track data) {
<tr
*
ngFor=
"let data of JobTable.data; let i = index"
>
<tr>
<td>
{{ i + 1 }}
</td>
<td>
{{ listOfJob.indexOf(data) + 1 }}
</td>
<td>
{{ data.type }}
</td>
<td>
{{ data.type }}
</td>
<td>
{{ data.title }}
</td>
<td>
{{ data.title }}
</td>
<td>
{{ data.company }}
</td>
<td>
{{ data.company }}
</td>
<td>
{{ data.location }}
</td>
<td>
{{ data.location }}
</td>
<td>
<td>
<nz-space>
<nz-space>
<button
class=
"tw-mr-2"
nz-button
nzType=
"primary"
nzSize=
"small"
(
click
)="
JobDetail
(
data
.
id
)"
>
<button
class=
"tw-mr-2"
nz-button
nzType=
"primary"
nzSize=
"small"
(
click
)="
JobDetail
(
data
.
id
)"
>
Xem chi tiết
Xem chi tiết
</button>
</button>
<button
<button
...
@@ -42,61 +49,31 @@
...
@@ -42,61 +49,31 @@
>
>
Sửa
Sửa
</button>
</button>
<button
nz-button
nzType=
"dashed"
nzSize=
"small"
nzDanger
(
click
)="
showDeleteConfirm
(
data
.
id
)"
>
<button
nz-button
nzType=
"dashed"
nzSize=
"small"
nzDanger
(
click
)="
showDeleteConfirm
(
data
.
id
)"
>
Xóa
Xóa
</button>
</button>
</nz-space>
</nz-space>
</td>
</td>
</tr>
</tr>
}
</tbody>
</tbody>
</nz-table>
</nz-table>
<app-modal-job
[
showModal
]="
showModal
"
[
jobData
]="
currentEditJob
"
[
isEdit
]="
isEdit
"
(
close
)="
onCloseModal
()"
(
submit
)="
onSubmit
()"
></app-modal-job>
<nz-modal
[(
nzVisible
)]="
isOpenModal
"
[
nzTitle
]="
isOnEdit
?
'
S
ử
a
c
ô
ng
vi
ệ
c
'
:
'
Chi
ti
ế
t
c
ô
ng
vi
ệ
c
'"
(
nzOnOk
)="
onOk
()"
(
nzOnCancel
)="
onCancel
()"
>
<app-view-job
<ng-container
*
nzModalContent
>
[
showViewModalComponent
]="
showViewModalComponent
"
<div
*
ngIf=
"selectedJob"
>
[
jobData
]="
currentViewJob
"
<form
nz-form
[
formGroup
]="
validateJobForm
"
>
(
close
)="
onCloseViewModal
()"
<nz-form-item>
></app-view-job>
<nz-form-label
[
nzSpan
]="
7
"
>
Loại công việc
</nz-form-label>
<nz-form-control
[
nzSpan
]="
17
"
>
<nz-select
formControlName=
"type"
placeholder=
"Loại công việc"
ngModel=
{{selectedJob.type}}
>
<nz-option
nzValue=
"Full Time"
nzLabel=
"Full Time"
></nz-option>
<nz-option
nzValue=
"Part Time"
nzLabel=
"Part Time"
></nz-option>
<nz-option
nzValue=
"Freelance"
nzLabel=
"Freelance"
></nz-option>
</nz-select>
</nz-form-control>
</nz-form-item>
<nz-form-item>
<nz-form-label
[
nzSpan
]="
7
"
>
Tên công việc
</nz-form-label>
<nz-form-control
[
nzSpan
]="
17
"
>
<input
class=
"tw-w-full tw-p-2 tw-bg-white tw-border tw-rounded-md focus:tw-outline-none focus:tw-ring-1 focus:tw-ring-blue-500 focus:tw-border-blue-500"
nz-input
formControlName=
"location"
nz-input
formControlName=
"title"
placeholder=
"Tên công việc"
[
ngModel
]="
selectedJob
.
title
"
/>
</nz-form-control>
</nz-form-item>
<nz-form-item>
<nz-form-label
[
nzSpan
]="
7
"
>
Công ty
</nz-form-label>
<nz-form-control
[
nzSpan
]="
17
"
>
<input
class=
"tw-w-full tw-p-2 tw-bg-white tw-border tw-rounded-md focus:tw-outline-none focus:tw-ring-1 focus:tw-ring-blue-500 focus:tw-border-blue-500"
nz-input
formControlName=
"location"
nz-input
formControlName=
"company"
placeholder=
"Công ty"
[
ngModel
]="
selectedJob
.
company
"
/>
</nz-form-control>
</nz-form-item>
<nz-form-item>
<nz-form-label
[
nzSpan
]="
7
"
>
Địa chỉ
</nz-form-label>
<nz-form-control
[
nzSpan
]="
17
"
>
<input
class=
"tw-w-full tw-p-2 tw-bg-white tw-border tw-rounded-md focus:tw-outline-none focus:tw-ring-1 focus:tw-ring-blue-500 focus:tw-border-blue-500"
nz-input
formControlName=
"location"
placeholder=
"Địa chỉ"
[
ngModel
]="
selectedJob
.
location
"
/>
</nz-form-control>
</nz-form-item>
<nz-form-item>
<nz-form-label
[
nzSpan
]="
7
"
>
Mô tả
</nz-form-label>
<nz-form-control
[
nzSpan
]="
17
"
>
<textarea
class=
"tw-w-full tw-h-40 tw-p-2 tw-bg-white tw-border tw-rounded-md focus:tw-outline-none focus:tw-ring-1 focus:tw-ring-blue-500 focus:tw-border-blue-500"
nz-input
formControlName=
"description"
placeholder=
"Mô tả công việc"
[
ngModel
]="
selectedJob
.
description
"
></textarea>
</nz-form-control>
</nz-form-item>
</form>
</div>
</ng-container>
</nz-modal>
</div>
</div>
src/app/+admin/Management/Job/Job.component.scss
deleted
100644 → 0
View file @
d3f8c729
src/app/+admin/Management/Job/Job.component.ts
View file @
5465e871
//#region imports
import
{
CommonModule
}
from
"@angular/common"
;
import
{
CommonModule
}
from
"@angular/common"
;
import
{
C
omponent
,
OnInit
}
from
"@angular/core"
;
import
{
C
hangeDetectionStrategy
,
ChangeDetectorRef
,
Component
,
HostListener
,
OnInit
}
from
"@angular/core"
;
import
{
JobService
}
from
"../../data-access/services/JobService.service"
;
import
{
JobService
}
from
"../../data-access/services/JobService.service"
;
import
{
NzTableModule
,
NzTableQueryParams
}
from
'ng-zorro-antd/table'
;
import
{
NzTableModule
}
from
'ng-zorro-antd/table'
;
import
{
catchError
,
finalize
,
of
,
tap
}
from
"rxjs"
;
import
{
catchError
,
finalize
,
of
,
tap
}
from
"rxjs"
;
import
{
JobModel
}
from
"../../data-access/model/jobModel.model"
;
import
{
JobModel
}
from
"../../data-access/model/jobModel.model"
;
import
{
NzDividerModule
}
from
'ng-zorro-antd/divider'
;
import
{
NzDividerModule
}
from
'ng-zorro-antd/divider'
;
import
{
NzButtonModule
}
from
"ng-zorro-antd/button"
;
import
{
NzButtonModule
}
from
"ng-zorro-antd/button"
;
import
{
NzSpaceModule
}
from
'ng-zorro-antd/space'
;
import
{
NzSpaceModule
}
from
'ng-zorro-antd/space'
;
import
{
NzModalModule
,
NzModalService
}
from
'ng-zorro-antd/modal'
;
import
{
NzModalModule
,
NzModalService
}
from
'ng-zorro-antd/modal'
;
import
{
FormBuilder
,
ReactiveFormsModule
,
Validators
}
from
"@angular/forms"
;
import
{
FormsModule
,
ReactiveFormsModule
}
from
"@angular/forms"
;
import
{
NzFormModule
}
from
"ng-zorro-antd/form"
;
import
{
NzFormModule
}
from
"ng-zorro-antd/form"
;
import
{
NzSelectModule
}
from
'ng-zorro-antd/select'
;
import
{
NzSelectModule
}
from
'ng-zorro-antd/select'
;
import
{
NzMessageService
}
from
'ng-zorro-antd/message'
;
import
{
NzAlertModule
}
from
"ng-zorro-antd/alert"
;
import
{
NzNotificationService
}
from
'ng-zorro-antd/notification'
;
import
{
ViewJobComponent
}
from
"./Action/JobDetail/JobDetail.component"
;
import
{
ModalJobComponent
}
from
"./Action/ModalJob/Modal.component"
;
//#endregion
//#region Component
@
Component
({
@
Component
({
selector
:
"meu-admin-job"
,
selector
:
"meu-admin-job"
,
templateUrl
:
"./Job.component.html"
,
templateUrl
:
"./Job.component.html"
,
standalone
:
true
,
standalone
:
true
,
imports
:
[
CommonModule
,
imports
:
[
CommonModule
,
NzTableModule
,
NzTableModule
,
NzDividerModule
,
NzDividerModule
,
NzButtonModule
,
NzButtonModule
,
NzSpaceModule
,
NzSpaceModule
,
NzModalModule
,
NzModalModule
,
NzModalModule
,
NzFormModule
,
NzFormModule
,
ReactiveFormsModule
,
ReactiveFormsModule
,
NzSelectModule
NzSelectModule
,
FormsModule
,
NzAlertModule
,
ViewJobComponent
,
ModalJobComponent
],
],
changeDetection
:
ChangeDetectionStrategy
.
OnPush
,
})
})
//#endregion
export
class
JobComponent
implements
OnInit
{
export
class
JobComponent
implements
OnInit
{
isMobileView
:
boolean
=
false
;
searchTerm
:
string
=
''
;
//#region Variable
//#region Variable
selectedJob
?:
JobModel
;
selectedJob
?:
JobModel
.
Job
;
listOfJob
:
JobModel
[]
=
[];
listOfJob
:
JobModel
.
Job
[]
=
[];
loading
=
true
;
message
=
""
;
showModal
:
boolean
=
false
;
//modal
isEdit
:
boolean
=
false
;
isOpenModal
=
false
;
showViewModalComponent
:
boolean
=
false
;
isOnEdit
=
false
;
isOnAdd
=
false
;
// Công việc hiện tại để chỉnh sửa hoặc xem chi tiết
validateJobForm
=
this
.
fb
.
group
({
currentEditJob
:
JobModel
.
Job
|
null
=
null
;
type
:
this
.
fb
.
control
(
''
,
[
Validators
.
required
]),
currentViewJob
:
JobModel
.
Job
|
null
=
null
;
create_at
:
this
.
fb
.
control
(
''
,
[
Validators
.
required
]),
company_url
:
this
.
fb
.
control
(
''
,
[
Validators
.
required
]),
company
:
this
.
fb
.
control
(
''
,
[
Validators
.
required
]),
location
:
this
.
fb
.
control
(
''
,
[
Validators
.
required
]),
title
:
this
.
fb
.
control
(
''
,
[
Validators
.
required
]),
description
:
this
.
fb
.
control
(
''
,
[
Validators
.
required
]),
});
//#endregion
//#endregion
//#region LoadData
//#region LoadData
constructor
(
private
fb
:
FormBuilder
,
private
jobService
:
JobService
,
private
modal
:
NzModalService
)
{
}
constructor
(
private
_cdr
:
ChangeDetectorRef
,
private
jobService
:
JobService
,
private
modal
:
NzModalService
,
private
message
:
NzMessageService
,
private
notification
:
NzNotificationService
)
{
}
loadDataFromServer
():
void
{
loadDataFromServer
():
void
{
console
.
log
(
"hello"
);
this
.
loading
=
true
;
this
.
jobService
.
jobsGet
().
pipe
(
this
.
jobService
.
jobsGet
().
pipe
(
tap
((
res
)
=>
{
tap
((
res
)
=>
{
if
(
res
.
responseData
)
if
(
res
.
responseData
)
{
this
.
listOfJob
=
res
.
responseData
.
rows
;
this
.
listOfJob
=
res
.
responseData
.
rows
;
}
this
.
_cdr
.
detectChanges
();
}),
}),
catchError
((
err
)
=>
{
catchError
((
err
)
=>
{
this
.
listOfJob
=
[];
this
.
listOfJob
=
[];
this
.
notification
.
error
(
'Lỗi'
,
'Không tải được dữ liệu công việc.'
);
return
of
(
null
);
return
of
(
null
);
}),
})
finalize
(()
=>
(
this
.
loading
=
false
))
).
subscribe
();
).
subscribe
();
}
}
ngOnInit
():
void
{
ngOnInit
():
void
{
this
.
loadDataFromServer
();
this
.
loadDataFromServer
();
this
.
checkScreenSize
();
// Kiểm tra kích thước màn hình ban đầu
}
}
//#endregion
//#region Handler
onCloseViewModal
():
void
{
resetForm
()
{
this
.
showViewModalComponent
=
false
;
this
.
validateJobForm
.
reset
();
this
.
selectedJob
=
{}
as
JobModel
;
}
}
EnableForm
()
{
onSubmit
():
void
{
this
.
validateJobForm
.
enable
();
this
.
isEdit
=
false
;
this
.
showModal
=
false
;
this
.
_cdr
.
detectChanges
();
this
.
loadDataFromServer
();
}
}
DisableForm
()
{
onCloseModal
():
void
{
this
.
validateJobForm
.
disable
()
;
this
.
showModal
=
false
;
}
}
onOk
():
void
{
//#endregion
console
.
log
(
this
.
onEdit
,
this
.
selectedJob
);
if
(
this
.
isOnEdit
&&
this
.
selectedJob
)
{
//#region Handlers
const
updatedJob
:
JobModel
=
{
id
:
this
.
selectedJob
.
id
,
// Handler xem chi tiết công việc
type
:
this
.
validateJobForm
.
value
.
type
??
undefined
,
handleViewJob
(
id
:
string
):
void
{
created_at
:
this
.
selectedJob
.
created_at
,
this
.
jobService
.
getJobById
(
id
)
company
:
this
.
validateJobForm
.
value
.
company
??
undefined
,
.
pipe
(
company_url
:
this
.
validateJobForm
.
value
.
company_url
??
undefined
,
location
:
this
.
validateJobForm
.
value
.
location
??
undefined
,
title
:
this
.
validateJobForm
.
value
.
title
??
undefined
,
description
:
this
.
validateJobForm
.
value
.
description
??
undefined
,
};
this
.
loading
=
true
;
this
.
jobService
.
editJob
(
updatedJob
).
pipe
(
tap
((
res
)
=>
{
tap
((
res
)
=>
{
this
.
message
=
res
.
message
;
this
.
showViewModalComponent
=
true
;
this
.
loadDataFromServer
();
this
.
currentViewJob
=
res
.
responseData
as
JobModel
.
Job
;
this
.
_cdr
.
detectChanges
();
}),
}),
catchError
((
err
)
=>
{
catchError
((
err
)
=>
{
console
.
error
(
err
);
this
.
message
.
error
(
err
.
message
||
'An error occurred while fetching job data.'
,
{
nzDuration
:
2500
}
);
return
of
(
null
);
return
of
(
null
);
})
})
).
subscribe
();
)
}
else
if
(
this
.
isOnAdd
&&
this
.
selectedJob
)
{
.
subscribe
();
const
newJob
:
JobModel
=
{
}
id
:
this
.
selectedJob
.
id
,
type
:
this
.
validateJobForm
.
value
.
type
??
undefined
,
// Handler xóa công việc
created_at
:
this
.
selectedJob
.
created_at
,
onDelete
(
id
:
string
):
void
{
company
:
this
.
validateJobForm
.
value
.
company
??
undefined
,
this
.
jobService
.
deleteJob
(
id
)
company_url
:
this
.
validateJobForm
.
value
.
company_url
??
undefined
,
.
pipe
(
location
:
this
.
validateJobForm
.
value
.
location
??
undefined
,
title
:
this
.
validateJobForm
.
value
.
title
??
undefined
,
description
:
this
.
validateJobForm
.
value
.
description
??
undefined
,
};
this
.
loading
=
true
;
this
.
jobService
.
addingNewJob
(
newJob
).
pipe
(
tap
((
res
)
=>
{
tap
((
res
)
=>
{
this
.
message
=
res
.
message
;
this
.
message
.
success
(
res
.
message
,
{
nzDuration
:
2500
});
this
.
_cdr
.
markForCheck
();
this
.
loadDataFromServer
();
this
.
loadDataFromServer
();
}),
}),
catchError
((
err
)
=>
{
catchError
((
err
)
=>
{
console
.
error
(
err
);
this
.
message
.
error
(
err
.
message
,
{
nzDuration
:
2500
}
);
return
of
(
null
);
return
of
(
null
);
})
})
).
subscribe
();
)
}
.
subscribe
();
this
.
isOpenModal
=
false
;
this
.
isOnEdit
=
false
;
this
.
isOnAdd
=
false
;
}
}
onCancel
():
void
{
// Xử lý khi nhấn nút "Thêm công việc"
this
.
isOpenModal
=
false
;
addJob
():
void
{
this
.
isOnEdit
=
fals
e
;
this
.
showModal
=
tru
e
;
}
}
//#endregion
//#region Action
JobDetail
(
id
:
string
)
{
this
.
DisableForm
();
this
.
isOnEdit
=
false
;
this
.
isOpenModal
=
true
;
this
.
isOnAdd
=
false
;
this
.
jobService
.
getJob
(
id
).
pipe
(
tap
((
res
)
=>
{
console
.
log
(
res
);
this
.
selectedJob
=
res
.
responseData
as
JobModel
;
}),
catchError
((
err
)
=>
{
console
.
log
(
err
);
return
of
(
null
);
}),
finalize
(()
=>
(
// Xử lý khi nhấn nút "Sửa"
this
.
loadDataFromServer
(),
this
.
loading
=
false
))
onEdit
(
data
:
JobModel
.
Job
):
void
{
).
subscribe
();
this
.
isEdit
=
true
;
}
this
.
currentEditJob
=
data
;
onDelete
(
id
:
string
)
{
this
.
showModal
=
true
;
this
.
jobService
.
deleteJob
(
id
).
pipe
(
tap
((
res
)
=>
{
this
.
loading
=
true
;
this
.
message
=
res
.
message
;
}),
catchError
((
err
)
=>
{
console
.
log
(
err
);
return
of
(
null
);
}),
finalize
(()
=>
(
this
.
loadDataFromServer
(),
this
.
loading
=
false
))
).
subscribe
();
}
onEdit
(
data
:
JobModel
)
{
this
.
EnableForm
();
this
.
isOpenModal
=
true
;
this
.
isOnEdit
=
true
;
this
.
isOnAdd
=
false
;
this
.
selectedJob
=
data
;
}
}
addJob
()
{
this
.
EnableForm
();
// Xử lý khi nhấn nút "Xem chi tiết"
this
.
resetForm
();
JobDetail
(
id
:
string
):
void
{
this
.
isOpenModal
=
true
;
this
.
handleViewJob
(
id
);
this
.
isOnEdit
=
false
;
this
.
isOnAdd
=
true
;
}
}
// Hiển thị hộp thoại xác nhận xóa
showDeleteConfirm
(
id
:
string
):
void
{
showDeleteConfirm
(
id
:
string
):
void
{
this
.
modal
.
confirm
({
this
.
modal
.
confirm
({
nzTitle
:
'Bạn có chắc rằng xóa công việc này không?'
,
nzTitle
:
'Bạn có chắc rằng xóa công việc này không?'
,
...
@@ -204,8 +160,25 @@ export class JobComponent implements OnInit {
...
@@ -204,8 +160,25 @@ export class JobComponent implements OnInit {
nzOkType
:
'primary'
,
nzOkType
:
'primary'
,
nzOkDanger
:
true
,
nzOkDanger
:
true
,
nzOnOk
:
()
=>
this
.
onDelete
(
id
),
nzOnOk
:
()
=>
this
.
onDelete
(
id
),
nzCancelText
:
'
t
rở về'
,
nzCancelText
:
'
T
rở về'
,
});
});
}
}
//#endregion
//#region HostListener
@
HostListener
(
'window:resize'
,
[
'$event'
])
onResize
(
event
:
any
)
{
this
.
checkScreenSize
();
}
checkScreenSize
()
{
this
.
isMobileView
=
window
.
innerWidth
<
768
;
this
.
_cdr
.
detectChanges
();
}
isMobile
():
boolean
{
return
this
.
isMobileView
;
}
//#endregion
//#endregion
}
}
src/app/+admin/data-access/model/jobModel.model.ts
View file @
5465e871
export
interface
JobModel
{
export
namespace
JobModel
{
id
:
string
;
export
interface
Job
{
type
?:
string
;
id
:
string
;
created_at
:
string
;
type
?:
string
;
company
?:
string
;
created_at
:
string
;
company_url
?:
string
;
company
?:
string
;
location
?:
string
;
company_url
?:
string
;
title
?:
string
;
location
?:
string
;
description
?:
string
;
title
?:
string
;
how_to_apply
?:
string
;
description
?:
string
;
company_logo
?:
string
;
how_to_apply
?:
string
;
company_logo
?:
string
;
}
export
interface
JobRequest
{
type
:
string
;
created_at
:
string
;
company
:
string
;
company_url
:
string
;
location
:
string
;
title
:
string
;
description
:
string
;
}
}
}
src/app/+admin/data-access/services/JobService.service.ts
View file @
5465e871
...
@@ -14,37 +14,24 @@ export class JobService {
...
@@ -14,37 +14,24 @@ export class JobService {
apiUrlById
=
environment
.
API_DOMAIN
+
'/jobById'
;
apiUrlById
=
environment
.
API_DOMAIN
+
'/jobById'
;
constructor
(
private
http
:
HttpClient
)
{}
constructor
(
private
http
:
HttpClient
)
{}
jobsGet
():
Observable
<
ResponseResult
<
Rows
<
JobModel
>>>
{
jobsGet
():
Observable
<
ResponseResult
<
Rows
<
JobModel
.
Job
>>>
{
return
this
.
http
.
get
<
ResponseResult
<
Rows
<
JobModel
>>>
(
this
.
apiUrl
);
return
this
.
http
.
get
<
ResponseResult
<
Rows
<
JobModel
.
Job
>>>
(
this
.
apiUrl
);
}
}
addingNewJob
(
data
:
JobModel
):
Observable
<
ResponseResult
<
JobModel
>>
{
addingNewJob
(
data
:
JobModel
.
JobRequest
):
Observable
<
ResponseResult
<
JobModel
.
Job
>>
{
const
headers
=
new
HttpHeaders
()
return
this
.
http
.
post
<
ResponseResult
<
JobModel
.
Job
>>
(
this
.
apiUrl
,
data
);
.
set
(
'Content-Type'
,
'application/json'
)
.
set
(
'Authorization'
,
`Bearer
${
localStorage
.
getItem
(
'token'
)}
`
);
return
this
.
http
.
post
<
ResponseResult
<
JobModel
>>
(
this
.
apiUrl
,
data
,
{
headers
});
}
}
deleteJob
(
id
:
string
):
Observable
<
ResponseResult
<
JobModel
>>
{
deleteJob
(
id
:
string
):
Observable
<
ResponseResult
<
JobModel
.
Job
>>
{
const
headers
=
new
HttpHeaders
()
const
url
=
`
${
this
.
apiUrl
}
/
${
id
}
`
;
.
set
(
'Content-Type'
,
'application/json'
)
return
this
.
http
.
delete
<
ResponseResult
<
JobModel
.
Job
>>
(
url
);
.
set
(
'Authorization'
,
`Bearer
${
localStorage
.
getItem
(
'token'
)}
`
);
const
url
=
`
${
this
.
apiUrl
}
/
${
id
}
`
;
return
this
.
http
.
delete
<
ResponseResult
<
JobModel
>>
(
url
,
{
headers
});
}
}
editJob
(
data
:
JobModel
):
Observable
<
ResponseResult
<
JobModel
>>
{
editJob
(
data
:
JobModel
.
Job
):
Observable
<
ResponseResult
<
JobModel
.
Job
>>
{
const
headers
=
new
HttpHeaders
()
.
set
(
'Content-Type'
,
'application/json'
)
.
set
(
'Authorization'
,
`Bearer
${
localStorage
.
getItem
(
'token'
)}
`
);
const
url
=
`
${
this
.
apiUrl
}
/
${
data
.
id
}
`
;
const
url
=
`
${
this
.
apiUrl
}
/
${
data
.
id
}
`
;
return
this
.
http
.
put
<
ResponseResult
<
JobModel
>>
(
url
,
data
,
{
headers
}
);
return
this
.
http
.
put
<
ResponseResult
<
JobModel
.
Job
>>
(
url
,
data
);
}
}
getJob
(
id
:
string
):
Observable
<
ResponseResult
<
JobModel
>>
{
getJobById
(
id
:
string
):
Observable
<
ResponseResult
<
JobModel
.
Job
>>
{
const
headers
=
new
HttpHeaders
()
.
set
(
'Content-Type'
,
'application/json'
)
.
set
(
'Authorization'
,
`Bearer
${
localStorage
.
getItem
(
'token'
)}
`
);
const
url
=
`
${
this
.
apiUrlById
}
/
${
id
}
`
;
const
url
=
`
${
this
.
apiUrlById
}
/
${
id
}
`
;
console
.
log
(
id
,
url
);
return
this
.
http
.
get
<
ResponseResult
<
JobModel
.
Job
>>
(
url
);
return
this
.
http
.
get
<
ResponseResult
<
JobModel
>>
(
url
,
{
headers
});
}
}
}
}
src/app/+admin/layout/feature/ui/Components/footer/footer.component.html
View file @
5465e871
...
@@ -5,21 +5,17 @@
...
@@ -5,21 +5,17 @@
<p>
Designed by Meu Solution
</p>
<p>
Designed by Meu Solution
</p>
<div
class=
"tw-flex tw-justify-center tw-space-x-4 tw-mt-4"
>
<div
class=
"tw-flex tw-justify-center tw-space-x-4 tw-mt-4"
>
<
a
href=
"#about"
class=
" hover:tw-text-blue-500"
>
About Us
</a
>
<
div
*
ngFor=
"let item of list"
>
<a
href=
"#contact"
class=
" hover:tw-text-blue-500"
>
Contact
</a>
<a
href=
"#{{item.href}}"
class=
" hover:tw-text-blue-500"
>
{{item.title}}
</a>
<
a
href=
"#privacy"
class=
" hover:tw-text-blue-500"
>
Privacy Policy
</a
>
<
/div
>
</div>
</div>
<div
class=
"tw-flex tw-justify-center tw-space-x-4 tw-mt-4"
>
<div
class=
"tw-flex tw-justify-center tw-space-x-4 tw-mt-4"
>
<a
href=
"#"
target=
"_blank"
class=
" hover:tw-text-blue-500"
>
<div
*
ngFor=
"let item of listIcon"
>
<i
nz-icon
nzType=
"facebook"
class=
"tw-text-2xl"
></i>
<a
href=
"#"
target=
"_blank"
class=
" hover:tw-text-blue-500"
>
</a>
<i
nz-icon
nzType=
"{{item}}"
class=
"tw-text-2xl"
></i>
<a
href=
"#"
target=
"_blank"
class=
" hover:tw-text-blue-500"
>
</a>
<i
nz-icon
nzType=
"twitter"
class=
"tw-text-2xl"
></i>
</div>
</a>
<a
href=
"#"
target=
"_blank"
class=
" hover:tw-text-blue-500"
>
<i
nz-icon
nzType=
"instagram"
class=
"tw-text-2xl"
></i>
</a>
</div>
</div>
</div>
</div>
</footer>
</footer>
...
...
src/app/+admin/layout/feature/ui/Components/footer/footer.component.ts
View file @
5465e871
...
@@ -13,5 +13,12 @@ import { NzLayoutModule } from 'ng-zorro-antd/layout';
...
@@ -13,5 +13,12 @@ import { NzLayoutModule } from 'ng-zorro-antd/layout';
changeDetection
:
ChangeDetectionStrategy
.
OnPush
,
changeDetection
:
ChangeDetectionStrategy
.
OnPush
,
})
})
export
class
FooterComponent
implements
OnInit
{
export
class
FooterComponent
implements
OnInit
{
list
:
{
href
:
string
,
title
:
string
}[]
=
[
{
href
:
'about'
,
title
:
'về chúng tôi'
},
{
href
:
'contact'
,
title
:
'liên hệ'
},
{
href
:
'privacy'
,
title
:
'chính sách bảo mật'
}
];
listIcon
:
string
[]
=
[
'facebook'
,
'instagram'
,
'twitter'
];
ngOnInit
():
void
{}
ngOnInit
():
void
{}
}
}
src/app/+admin/layout/feature/ui/Components/header/header.component.html
deleted
100644 → 0
View file @
d3f8c729
<header
class=
"tw-flex tw-justify-between tw-items-center tw-p-4 tw-bg-gray-900"
>
<h3
class=
"tw-text-2xl"
>
Role: {{role}}
</h3>
</header>
src/app/+admin/layout/feature/ui/Components/header/header.component.ts
deleted
100644 → 0
View file @
d3f8c729
import
{
CommonModule
}
from
'@angular/common'
;
import
{
ChangeDetectionStrategy
,
Component
,
Input
,
OnInit
}
from
'@angular/core'
;
import
{
NzLayoutModule
}
from
'ng-zorro-antd/layout'
;
@
Component
({
selector
:
'meu-admin-header'
,
standalone
:
true
,
imports
:
[
CommonModule
,
NzLayoutModule
],
templateUrl
:
'./header.component.html'
,
changeDetection
:
ChangeDetectionStrategy
.
OnPush
,
})
export
class
HeaderComponent
implements
OnInit
{
@
Input
()
role
=
''
;
ngOnInit
():
void
{}
}
src/app/+admin/layout/feature/ui/layout.component.html
View file @
5465e871
<nz-layout>
<nz-layout
class=
"tw-h-screen tw-flex tw-flex-col"
>
<nz-sider
nzCollapsible
[
nzTrigger
]="
null
"
>
<nz-header
class=
"tw-flex tw-justify-between tw-items-center tw-px-4 tw-bg-[#1f487725] tw-shadow-md"
>
<div
class=
"logo"
></div>
<div
class=
"tw-flex tw-items-center"
>
<ul
nz-menu
nzTheme=
"dark"
nzMode=
"inline"
>
<button
<li
nz-menu-item
routerLink=
"/home"
>
nz-button
Trang chủ
nzType=
"text"
</li>
class=
"tw-block md:tw-hidden"
<li
nz-menu-item
routerLink=
"/admin/job-management/Job"
>
(
click
)="
toggleSider
()"
Danh sách công việc
>
</li>
<span
nz-icon
[
nzType
]="
isCollapsed
?
'
menu-unfold
'
:
'
menu-fold
'"
nzTheme=
"outline"
></span>
<li
nz-menu-item
(
click
)="
logout
()"
>
</button>
<div
class=
"tw-ml-2 tw-text-xl tw-font-bold"
>
MeU Solution
</div>
</div>
<div
class=
"tw-hidden md:tw-flex tw-items-center"
>
<button
nz-button
nzType=
"default"
(
click
)="
logout
()"
>
<span
nz-icon
nzType=
"logout"
nzTheme=
"outline"
></span>
<span
nz-icon
nzType=
"logout"
nzTheme=
"outline"
></span>
Đăng xuất
<span
class=
"tw-ml-2"
>
Đăng xuất
</span>
</li>
</button>
</ul>
</div>
</nz-sider>
</nz-header>
<nz-layout>
<nz-content>
<nz-layout
class=
"tw-flex-1"
>
<router-outlet></router-outlet>
<nz-sider
</nz-content>
nzCollapsible
<meu-footer></meu-footer>
[(
nzCollapsed
)]="
isCollapsed
"
[
nzBreakpoint
]="'
lg
'"
[
nzWidth
]="
200
"
[
nzCollapsedWidth
]="
80
"
class=
"tw-bg-gray-800"
>
<ul
nz-menu
nzTheme=
"dark"
nzMode=
"inline"
>
<li
nz-menu-item
routerLink=
"/home"
routerLinkActive=
"tw-bg-gray-700"
>
<span
nz-icon
nzType=
"home"
nzTheme=
"outline"
></span>
<span
class=
"tw-ml-2"
>
Trang chủ
</span>
</li>
<li
nz-menu-item
routerLink=
"/admin/job-management/Job"
routerLinkActive=
"tw-bg-gray-700"
>
<span
nz-icon
nzType=
"unordered-list"
nzTheme=
"outline"
></span>
<span
class=
"tw-ml-2"
>
Danh sách công việc
</span>
</li>
</ul>
</nz-sider>
<nz-layout
class=
"tw-flex-1 tw-bg-gray-100"
>
<nz-content
>
<router-outlet></router-outlet>
</nz-content>
<meu-footer
>
©
2024 Công ty của bạn. Bảo lưu mọi quyền.
</meu-footer>
</nz-layout>
</nz-layout>
</nz-layout>
</nz-layout>
</nz-layout>
src/app/+admin/layout/feature/ui/layout.component.scss
View file @
5465e871
...
@@ -15,7 +15,9 @@
...
@@ -15,7 +15,9 @@
background
:
rgba
(
255
,
255
,
255
,
0
.2
);
background
:
rgba
(
255
,
255
,
255
,
0
.2
);
margin
:
16px
;
margin
:
16px
;
}
}
::ng-deep
.ant-drawer-body
{
padding
:
0
;
}
nz-header
{
nz-header
{
background
:
#fff
;
background
:
#fff
;
padding
:
0
;
padding
:
0
;
...
...
src/app/+admin/layout/feature/ui/layout.component.ts
View file @
5465e871
...
@@ -3,7 +3,6 @@ import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
...
@@ -3,7 +3,6 @@ import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
import
{
NzLayoutModule
}
from
'ng-zorro-antd/layout'
;
import
{
NzLayoutModule
}
from
'ng-zorro-antd/layout'
;
import
{
CommonModule
,
DatePipe
}
from
'@angular/common'
;
import
{
CommonModule
,
DatePipe
}
from
'@angular/common'
;
import
{
HeaderComponent
}
from
'./Components/header/header.component'
;
import
{
FooterComponent
}
from
'./Components/footer/footer.component'
;
import
{
FooterComponent
}
from
'./Components/footer/footer.component'
;
import
{
NzButtonModule
}
from
'ng-zorro-antd/button'
;
import
{
NzButtonModule
}
from
'ng-zorro-antd/button'
;
import
{
NzBreadCrumbModule
}
from
'ng-zorro-antd/breadcrumb'
;
import
{
NzBreadCrumbModule
}
from
'ng-zorro-antd/breadcrumb'
;
...
@@ -20,20 +19,23 @@ import { AuthService } from '../../../../+login/data-access/Services/Auth.Servic
...
@@ -20,20 +19,23 @@ import { AuthService } from '../../../../+login/data-access/Services/Auth.Servic
RouterLink
,
RouterLink
,
CommonModule
,
CommonModule
,
RouterModule
,
RouterModule
,
HeaderComponent
,
FooterComponent
,
NzButtonModule
,
NzButtonModule
,
NzBreadCrumbModule
,
NzBreadCrumbModule
,
NzIconModule
,
NzLayoutModule
,
NzLayoutModule
,
NzMenuModule
,
NzMenuModule
,
NzToolTipModule
,
NzToolTipModule
,
NzIconModule
,
FooterComponent
],
],
styleUrls
:
[
'./layout.component.scss'
],
styleUrls
:
[
'./layout.component.scss'
],
templateUrl
:
'./layout.component.html'
,
templateUrl
:
'./layout.component.html'
,
changeDetection
:
ChangeDetectionStrategy
.
OnPush
,
changeDetection
:
ChangeDetectionStrategy
.
OnPush
,
})
})
export
class
AdminLayoutComponent
implements
OnInit
{
export
class
AdminLayoutComponent
implements
OnInit
{
isCollapsed
=
false
;
toggleSider
():
void
{
this
.
isCollapsed
=
!
this
.
isCollapsed
;
}
constructor
(
private
authentication
:
AuthService
)
{}
constructor
(
private
authentication
:
AuthService
)
{}
ngOnInit
():
void
{
ngOnInit
():
void
{
}
}
...
...
src/app/+home/component/JobCard/JobCard.component.html
deleted
100644 → 0
View file @
d3f8c729
<nz-card
*
ngIf=
"job"
[
nzTitle
]="
job
.
title
"
[
nzExtra
]="
extraTemplate
"
[
nzBodyStyle
]="{'
padding
'
:
'
8px
'}"
>
<p>
loại công việc: {{job.type}}
</p>
<p>
Công ty: {{job.company}}
</p>
<p>
Địa chỉ làm việc: {{job.location}}
</p>
</nz-card>
<ng-template
#
extraTemplate
>
<a
(
click
)="
onShowDetail
()"
>
Chi tiết
</a>
</ng-template>
<nz-modal
[(
nzVisible
)]="
isModalVisible
"
nzTitle=
{{job?.title}}
nzOkText=
"Ứng tuyển"
nzCancelText=
"Trở về"
(
nzOnOk
)="
onOk
()"
(
nzOnCancel
)="
onCancel
()"
>
<ng-container
*
nzModalContent
>
<p>
<strong>
Chi tiết công việc
</strong>
</p>
<p>
<label>
Loại công việc
</label>
{{ job?.type }}
</p>
<p>
<label>
Ngày tạo
</label>
{{ job?.created_at| date: 'dd/MM/yyyy' }}
</p>
<p>
<label>
Link công ty
</label>
{{ job?.company_url }}
</p>
<div>
<strong>
Mô tả:
</strong>
<div
[
innerHTML
]="
job
?.
description
"
></div>
</div>
</ng-container>
</nz-modal>
src/app/+home/component/JobCard/JobCard.component.ts
deleted
100644 → 0
View file @
d3f8c729
import
{
ChangeDetectionStrategy
,
Component
,
Input
}
from
"@angular/core"
;
import
{
NzCardModule
}
from
"ng-zorro-antd/card"
;
import
{
Job
}
from
"../../data-access/model/Job.model"
;
import
{
CommonModule
}
from
"@angular/common"
;
import
{
NzModalModule
}
from
"ng-zorro-antd/modal"
;
@
Component
({
selector
:
'job-card'
,
templateUrl
:
'./JobCard.component.html'
,
imports
:
[
NzCardModule
,
CommonModule
,
NzModalModule
],
standalone
:
true
,
changeDetection
:
ChangeDetectionStrategy
.
OnPush
,
})
export
class
JobCardComponent
{
@
Input
()
job
?:
Job
;
isModalVisible
=
false
;
onShowDetail
()
{
this
.
isModalVisible
=
true
;
}
onOk
()
{
this
.
isModalVisible
=
false
}
onCancel
(){
this
.
isModalVisible
=
false
}
}
src/app/+home/component/JobDetail/JobDetail.component.html
0 → 100644
View file @
5465e871
<div
*
ngIf=
"Job; else loadingOrError"
>
<div
class=
"header flex items-center space-x-4"
>
<img
*
ngIf=
"Job.company_logo"
[
src
]="
Job
.
company_logo
"
alt=
"{{ Job.company }} Logo"
class=
"company-logo w-16 h-16 object-contain"
/>
<h2
class=
"tw-text-2xl tw-font-bold"
>
{{ Job.title }}
</h2>
</div>
<div
class=
"details mt-4"
>
<p
*
ngIf=
"Job.type"
><strong>
Loại công việc:
</strong>
{{ Job.type }}
</p>
<p
*
ngIf=
"Job.company"
>
<strong>
Công ty:
</strong>
<a>
{{ Job.company }}
</a
>
<span
*
ngIf=
"!Job.company_url"
>
{{ Job.company }}
</span>
</p>
<p
*
ngIf=
"Job.location"
><strong>
Vị trí:
</strong>
{{ Job.location }}
</p>
<p
class=
"tw-text-sm tw-text-gray-500"
>
<strong>
Ngày đăng:
</strong>
{{ Job.created_at | date : "medium" }}
</p>
</div>
<hr
class=
"my-4"
/>
<div
class=
"description mt-6"
>
<p
[
innerHTML
]="
Job
.
description
"
></p>
</div>
</div>
<ng-template
#
loadingOrError
>
<div
class=
"status-message"
>
<p
*
ngIf=
"Job === null"
class=
"error text-red-500"
>
Có lỗi khi tải thông tin công việc.
</p>
<p
*
ngIf=
"Job === undefined"
class=
"loading"
>
Đang tải...
</p>
</div>
</ng-template>
src/app/+home/component/JobDetail/JobDetail.component.ts
0 → 100644
View file @
5465e871
import
{
ChangeDetectionStrategy
,
ChangeDetectorRef
,
Component
,
Input
,
OnInit
}
from
"@angular/core"
;
import
{
Job
}
from
"../../data-access/model/Job.model"
;
import
{
JobService
}
from
"../../data-access/service/Job.service"
;
import
{
catchError
,
of
,
tap
}
from
"rxjs"
;
import
{
CommonModule
}
from
"@angular/common"
;
import
{
FormsModule
}
from
"@angular/forms"
;
@
Component
(
{
selector
:
'app-home-job-detail'
,
templateUrl
:
'./JobDetail.component.html'
,
standalone
:
true
,
imports
:
[
CommonModule
,
FormsModule
],
changeDetection
:
ChangeDetectionStrategy
.
OnPush
,
}
)
export
class
JobDetailComponent
implements
OnInit
{
@
Input
()
id
:
string
=
""
;
Job
?:
Job
;
constructor
(
private
JobService
:
JobService
,
private
_cdr
:
ChangeDetectorRef
)
{
}
ngOnInit
():
void
{
this
.
JobService
.
jobGetById
(
this
.
id
).
pipe
(
tap
((
data
)
=>
{
this
.
Job
=
data
.
responseData
as
Job
;
this
.
_cdr
.
markForCheck
();
}),
catchError
((
error
)
=>
{
return
of
(
null
);
})
).
subscribe
();
}
}
src/app/+home/component/JobList/JobList.component.html
0 → 100644
View file @
5465e871
<div
class=
"tw-flex tw-justify-center tw-py-8"
>
<h1
class=
"tw-text-2xl tw-font-bold"
>
Danh sách công việc
</h1>
</div>
<div
class=
"tw-flex tw-justify-center tw-space-x-4 tw-mb-4"
>
<input
nz-input
placeholder=
"Tìm kiếm"
class=
"tw-w-full tw-max-w-md"
[(
ngModel
)]="
searchTerm
"
(
ngModelChange
)="
search
()"
/>
</div>
<div
class=
"tw-flex tw-justify-center"
>
<nz-list
nzItemLayout=
"vertical"
[
nzDataSource
]="
displayedJobs
"
class=
"tw-w-full tw-max-w-4xl"
nzBordered
>
<nz-list-item
*
ngFor=
"let item of displayedJobs"
class=
"tw-bg-[#3174c025] tw-shadow tw-rounded tw-p-4"
>
<nz-list-item-meta>
<nz-list-item-meta-title>
<a
href=
"/DetailJob/{{ item?.id }}"
class=
"tw-text-base tw-font-bold"
>
{{ item?.title }}
</a>
</nz-list-item-meta-title>
</nz-list-item-meta>
<ul
nz-list-item-actions
class=
"tw-flex tw-flex-wrap tw-gap-4 tw-mt-2"
>
<nz-list-item-action
*
ngIf=
"item?.type"
>
<i
nz-icon
nzType=
"star-o"
class=
"tw-mr-1"
></i>
{{ item?.type }}
</nz-list-item-action>
<nz-list-item-action
*
ngIf=
"item?.location"
>
<i
nz-icon
nzType=
"environment-o"
class=
"tw-mr-1"
></i>
{{ item?.location }}
</nz-list-item-action>
<nz-list-item-action
*
ngIf=
"item?.created_at"
>
<i
nz-icon
nzType=
"calendar-o"
class=
"tw-mr-1"
></i>
{{ item?.created_at | date:'medium' }}
</nz-list-item-action>
</ul>
</nz-list-item>
</nz-list>
</div>
<div
class=
"tw-flex tw-justify-center tw-my-8"
>
<nz-pagination
[
nzPageIndex
]="
currentPage
"
[
nzPageSize
]="
pageSize
"
[
nzTotal
]="
filteredJobs
.
length
"
(
nzPageIndexChange
)="
onPageChange
($
event
)"
[
nzShowSizeChanger
]="
false
"
>
</nz-pagination>
</div>
src/app/+home/component/JobList/JobList.component.ts
0 → 100644
View file @
5465e871
// JobListCard.component.ts
import
{
ChangeDetectionStrategy
,
ChangeDetectorRef
,
Component
,
OnInit
,
}
from
"@angular/core"
;
import
{
NzDividerModule
}
from
"ng-zorro-antd/divider"
;
import
{
NzGridModule
}
from
"ng-zorro-antd/grid"
;
import
{
JobModel
}
from
"../../../+admin/data-access/model/jobModel.model"
;
import
{
JobService
}
from
"../../data-access/service/Job.service"
;
import
{
tap
}
from
"rxjs/operators"
;
import
{
CommonModule
}
from
"@angular/common"
;
import
{
NzListModule
}
from
"ng-zorro-antd/list"
;
import
{
NzCardModule
}
from
"ng-zorro-antd/card"
;
import
{
NzTableModule
}
from
"ng-zorro-antd/table"
;
import
{
NzPaginationModule
}
from
"ng-zorro-antd/pagination"
;
import
{
FormsModule
}
from
"@angular/forms"
;
import
{
NzInputModule
}
from
"ng-zorro-antd/input"
;
import
{
NzButtonModule
}
from
"ng-zorro-antd/button"
;
import
{
NzIconModule
}
from
"ng-zorro-antd/icon"
;
import
{
RouterLink
}
from
"@angular/router"
;
@
Component
({
selector
:
"app-home-list-job"
,
templateUrl
:
"./JobList.component.html"
,
imports
:
[
CommonModule
,
FormsModule
,
NzDividerModule
,
NzGridModule
,
NzListModule
,
NzCardModule
,
NzTableModule
,
NzPaginationModule
,
NzInputModule
,
NzButtonModule
,
NzIconModule
,
],
standalone
:
true
,
changeDetection
:
ChangeDetectionStrategy
.
OnPush
,
})
export
class
JobListCardComponent
implements
OnInit
{
listOfJob
:
JobModel
.
Job
[]
=
[];
filteredJobs
:
JobModel
.
Job
[]
=
[];
displayedJobs
:
JobModel
.
Job
[]
=
[];
searchTerm
:
string
=
""
;
currentPage
:
number
=
1
;
pageSize
:
number
=
10
;
constructor
(
private
jobService
:
JobService
,
private
_changeDetectorRef
:
ChangeDetectorRef
)
{}
ngOnInit
()
{
this
.
fetchJobs
();
}
fetchJobs
():
void
{
this
.
jobService
.
jobsGet
().
pipe
(
tap
((
res
)
=>
{
this
.
listOfJob
=
res
.
responseData
?.
rows
as
JobModel
.
Job
[];
this
.
filteredJobs
=
[...
this
.
listOfJob
];
this
.
updateDisplayedJobs
();
this
.
_changeDetectorRef
.
markForCheck
();
})
).
subscribe
();
}
search
():
void
{
if
(
this
.
searchTerm
.
trim
())
{
const
term
=
this
.
searchTerm
.
trim
().
toLowerCase
();
this
.
filteredJobs
=
this
.
listOfJob
.
filter
((
job
)
=>
(
job
.
title
&&
job
.
title
.
toLowerCase
().
includes
(
term
))
||
(
job
.
type
&&
job
.
type
.
toLowerCase
().
includes
(
term
))
||
(
job
.
location
&&
job
.
location
.
toLowerCase
().
includes
(
term
))
);
}
else
{
this
.
filteredJobs
=
[...
this
.
listOfJob
];
}
this
.
currentPage
=
1
;
this
.
updateDisplayedJobs
();
this
.
_changeDetectorRef
.
markForCheck
();
}
onPageChange
(
page
:
number
):
void
{
this
.
currentPage
=
page
;
this
.
updateDisplayedJobs
();
this
.
_changeDetectorRef
.
markForCheck
();
}
updateDisplayedJobs
():
void
{
const
startIndex
=
(
this
.
currentPage
-
1
)
*
this
.
pageSize
;
const
endIndex
=
startIndex
+
this
.
pageSize
;
this
.
displayedJobs
=
this
.
filteredJobs
.
slice
(
startIndex
,
endIndex
);
}
}
src/app/+home/component/JobListCard/JobListCard.component.html
deleted
100644 → 0
View file @
d3f8c729
<nz-list
nzGrid
>
<div
nz-row
[
nzGutter
]="
16
"
>
@for (item of listOfJob; track item) {
<div
nz-col
[
nzSpan
]="
6
"
>
<nz-list-item>
<button
nz-button
class=
"tw-w-full"
>
<job-card
[
job
]="
item
"
></job-card>
</button>
</nz-list-item>
</div>
}
</div>
</nz-list>
src/app/+home/component/JobListCard/JobListCard.component.ts
deleted
100644 → 0
View file @
d3f8c729
import
{
ChangeDetectionStrategy
,
ChangeDetectorRef
,
Component
,
OnInit
}
from
"@angular/core"
;
import
{
JobCardComponent
}
from
"../JobCard/JobCard.component"
;
import
{
NzDividerModule
}
from
"ng-zorro-antd/divider"
;
import
{
NzGridModule
}
from
"ng-zorro-antd/grid"
;
import
{
JobModel
}
from
"../../../+admin/data-access/model/jobModel.model"
;
import
{
JobService
}
from
"../../data-access/service/Job.service"
;
import
{
tap
}
from
"rxjs"
;
import
{
CommonModule
}
from
"@angular/common"
;
import
{
NzListModule
}
from
'ng-zorro-antd/list'
;
import
{
NzCardModule
}
from
"ng-zorro-antd/card"
;
import
{
RouterLink
}
from
"@angular/router"
;
@
Component
({
selector
:
'app-home-list-job'
,
templateUrl
:
'./JobListCard.component.html'
,
imports
:
[
CommonModule
,
NzDividerModule
,
NzGridModule
,
JobCardComponent
,
NzGridModule
,
NzListModule
,
NzCardModule
,
RouterLink
],
standalone
:
true
,
changeDetection
:
ChangeDetectionStrategy
.
OnPush
,
})
export
class
JobListCardComponent
implements
OnInit
{
listOfJob
?:
JobModel
[]
;
constructor
(
private
jobService
:
JobService
,
private
_changeDetectorRef
:
ChangeDetectorRef
,)
{}
ngOnInit
()
{
this
.
jobService
.
jobsGet
().
pipe
(
tap
((
res
)
=>
{
this
.
listOfJob
=
res
.
responseData
?.
rows
as
JobModel
[];
this
.
_changeDetectorRef
.
markForCheck
();
console
.
log
(
this
.
listOfJob
);
}),
).
subscribe
();
}
}
src/app/+home/data-access/service/Job.service.ts
View file @
5465e871
...
@@ -10,8 +10,13 @@ import { HttpClient } from "@angular/common/http";
...
@@ -10,8 +10,13 @@ import { HttpClient } from "@angular/common/http";
})
})
export
class
JobService
{
export
class
JobService
{
apiUrl
=
environment
.
API_DOMAIN
+
'/jobs'
;
apiUrl
=
environment
.
API_DOMAIN
+
'/jobs'
;
apiUrlById
=
environment
.
API_DOMAIN
+
'/jobById'
;
constructor
(
private
http
:
HttpClient
)
{}
constructor
(
private
http
:
HttpClient
)
{}
jobsGet
():
Observable
<
ResponseResult
<
Rows
<
Job
>>>
{
jobsGet
():
Observable
<
ResponseResult
<
Rows
<
Job
>>>
{
return
this
.
http
.
get
<
ResponseResult
<
Rows
<
Job
>>>
(
this
.
apiUrl
);
return
this
.
http
.
get
<
ResponseResult
<
Rows
<
Job
>>>
(
this
.
apiUrl
);
}
}
jobGetById
(
id
:
string
):
Observable
<
ResponseResult
<
Job
>>
{
return
this
.
http
.
get
<
ResponseResult
<
Job
>>
(
`
${
this
.
apiUrlById
}
/
${
id
}
`
);
}
}
}
src/app/+login/data-access/Services/Auth.Service.ts
View file @
5465e871
...
@@ -18,7 +18,6 @@ export class AuthService {
...
@@ -18,7 +18,6 @@ export class AuthService {
return
this
.
http
.
post
<
any
>
(
this
.
apiUrl
,
body
,
{
headers
}).
pipe
(
return
this
.
http
.
post
<
any
>
(
this
.
apiUrl
,
body
,
{
headers
}).
pipe
(
tap
((
response
)
=>
{
tap
((
response
)
=>
{
localStorage
.
setItem
(
'token'
,
response
.
responseData
.
token
);
localStorage
.
setItem
(
'token'
,
response
.
responseData
.
token
);
localStorage
.
setItem
(
'expirationTime'
,
response
.
responseData
.
expirationTime
);
localStorage
.
setItem
(
'role'
,
response
.
responseData
.
role
);
localStorage
.
setItem
(
'role'
,
response
.
responseData
.
role
);
})
})
);
);
...
@@ -26,7 +25,6 @@ export class AuthService {
...
@@ -26,7 +25,6 @@ export class AuthService {
logout
():
void
{
logout
():
void
{
localStorage
.
removeItem
(
'token'
);
localStorage
.
removeItem
(
'token'
);
localStorage
.
removeItem
(
'expirationTime'
);
localStorage
.
removeItem
(
'role'
);
localStorage
.
removeItem
(
'role'
);
this
.
router
.
navigate
([
'/login'
]);
this
.
router
.
navigate
([
'/login'
]);
}
}
...
...
src/app/+login/feature/login.component.html
View file @
5465e871
<div
class=
"tw-flex tw-items-center tw-justify-center tw-min-h-screen tw-bg-gray-100"
>
<div
class=
"tw-flex tw-items-center tw-justify-center tw-min-h-screen tw-bg-gray-100"
>
<form
nz-form
[
formGroup
]="
loginForm
"
class=
"tw-login-form tw-bg-white tw-p-8 tw-shadow-lg tw-rounded-lg tw-w-full tw-max-w-md"
(
ngSubmit
)="
onSubmit
()"
>
<form
nz-form
[
formGroup
]="
loginForm
"
class=
"tw-login-form tw-bg-white tw-p-8 tw-shadow-lg tw-rounded-lg tw-w-full tw-max-w-md"
(
ngSubmit
)="
onSubmit
()"
>
<nz-form-text
class=
"tw-text-4xl tw-font-bold tw-text-center tw-mb-6"
>
LOGIN
</nz-form-text>
<nz-form-text
class=
"tw-text-4xl tw-font-bold tw-text-center tw-mb-6"
>
Đăng nhập
</nz-form-text>
<nz-form-item>
<nz-form-item>
<nz-form-control
nzErrorTip=
"
Please input your username
!"
>
<nz-form-control
nzErrorTip=
"
Nhập tên tài khoản
!"
>
<nz-input-group
nzPrefixIcon=
"user"
>
<nz-input-group
nzPrefixIcon=
"user"
>
<input
type=
"text"
nz-input
formControlName=
"userName"
placeholder=
"
Username
"
/>
<input
type=
"text"
nz-input
formControlName=
"userName"
placeholder=
"
tên tài khoản
"
/>
</nz-input-group>
</nz-input-group>
</nz-form-control>
</nz-form-control>
</nz-form-item>
</nz-form-item>
<nz-form-item>
<nz-form-item>
<nz-form-control
nzErrorTip=
"
Please input your Password
!"
>
<nz-form-control
nzErrorTip=
"
Tối thiểu 6 ký tự
!"
>
<nz-input-group
nzPrefixIcon=
"lock"
>
<nz-input-group
nzPrefixIcon=
"lock"
>
<input
type=
"password"
nz-input
formControlName=
"password"
placeholder=
"
Password
"
minlength=
"6"
/>
<input
type=
"password"
nz-input
formControlName=
"password"
placeholder=
"
Mật khẩu
"
minlength=
"6"
/>
</nz-input-group>
</nz-input-group>
</nz-form-control>
</nz-form-control>
</nz-form-item>
</nz-form-item>
<p
*
ngIf=
"MgsError"
class=
"error-message tw-text-center"
>
{{MgsError}}
</p>
<p
*
ngIf=
"MgsError"
class=
"error-message tw-text-center"
>
{{MgsError}}
</p>
<button
nz-button
class=
"login-form-button login-form-margin"
[
nzType
]="'
primary
'"
>
Log in
</button>
<button
nz-button
class=
"login-form-button login-form-margin"
[
nzType
]="'
primary
'"
>
Đăng nhập
</button>
</form>
</form>
</div>
</div>
src/app/+login/feature/login.component.ts
View file @
5465e871
...
@@ -62,14 +62,11 @@ export class LoginComponent implements OnInit {
...
@@ -62,14 +62,11 @@ export class LoginComponent implements OnInit {
this
.
authService
.
login
(
this
.
loginForm
.
value
.
userName
,
this
.
loginForm
.
value
.
password
).
pipe
(
this
.
authService
.
login
(
this
.
loginForm
.
value
.
userName
,
this
.
loginForm
.
value
.
password
).
pipe
(
catchError
((
err
)
=>
{
catchError
((
err
)
=>
{
this
.
MgsError
=
err
.
message
;
this
.
MgsError
=
err
.
message
;
console
.
log
(
this
.
MgsError
);
console
.
log
(
err
);
return
of
(
null
);
return
of
(
null
);
}),
}),
finalize
(()
=>
{
finalize
(()
=>
{
if
(
this
.
authService
.
isLoggedIn
())
if
(
this
.
authService
.
isLoggedIn
()){
this
.
authService
.
loginSussces
();
this
.
authService
.
loginSussces
();}
console
.
log
(
'Login request completed'
);
})
})
).
subscribe
();
).
subscribe
();
}
}
...
...
src/app/+shell/feature/shell.routes.ts
View file @
5465e871
...
@@ -24,7 +24,13 @@ export const shellRoutes: Routes = [
...
@@ -24,7 +24,13 @@ export const shellRoutes: Routes = [
path
:
'ListJob'
,
path
:
'ListJob'
,
canActivate
:
[],
canActivate
:
[],
loadComponent
:
()
=>
loadComponent
:
()
=>
import
(
'../../+home/component/JobListCard/JobListCard.component'
).
then
((
m
)
=>
m
.
JobListCardComponent
),
import
(
'../../+home/component/JobList/JobList.component'
).
then
((
m
)
=>
m
.
JobListCardComponent
),
},
{
path
:
'DetailJob/:id'
,
canActivate
:
[],
loadComponent
:
()
=>
import
(
'../../+home/component/JobDetail/JobDetail.component'
).
then
((
m
)
=>
m
.
JobDetailComponent
),
},
},
],
],
...
...
src/app/+shell/ui/components/header/feature/header.component.html
View file @
5465e871
<header
class=
"tw-bg-gray-900 tw-py-4"
>
<header
class=
"tw-bg-gray-900 tw-py-4
md:tw-px-4
"
>
<div
class=
"tw-container tw-mx-auto tw-flex tw-justify-between tw-items-center tw-px-4"
>
<div
class=
"tw-container tw-mx-auto tw-flex tw-justify-between tw-items-center tw-px-4
md:tw-px-0
"
>
<div
class=
"tw-text-3xl tw-font-bold"
>
<div
class=
"tw-text-3xl tw-font-bold"
>
<a
href=
"/home"
class=
"hover:tw-text-gray-400"
>
MeU Solution
</a>
<a
href=
"/home"
class=
"hover:tw-text-gray-400"
>
MeU Solution
</a>
</div>
</div>
...
@@ -7,12 +7,13 @@
...
@@ -7,12 +7,13 @@
<nav
class=
"tw-bg-gray-900 tw-py-2"
>
<nav
class=
"tw-bg-gray-900 tw-py-2"
>
<div
class=
"tw-container tw-mx-auto"
>
<div
class=
"tw-container tw-mx-auto"
>
<ul
class=
"tw-flex tw-space-x-8 tw-justify-center"
>
<ul
class=
"tw-flex tw-space-x-8 tw-justify-center
md:tw-justify-end
"
>
<
li><a
href=
"/home"
class=
"tw-text-gray-300 hover:tw-text-white"
>
Trang chủ
</a></li
>
<
div
*
ngFor=
"let item of list"
>
<li><a
href=
"/ListJob"
class=
"tw-text-gray-300 hover:tw-text-white"
>
Danh sách công việc
</a></li>
<li><a
href=
"/{{item.href}}"
class=
"tw-text-gray-300 hover:tw-text-white"
>
{{item.title}}
</a></li>
<
li><a
href=
"/admin"
class=
"tw-text-gray-300 hover:tw-text-white"
>
Admin
</a></li
>
<
/div
>
<li><a
(
click
)="
logout
()"
class=
"tw-text-gray-300 hover:tw-text-white"
>
Đăng xuất
</a></li>
<li><a
(
click
)="
logout
()"
class=
"tw-text-gray-300 hover:tw-text-white"
>
Đăng xuất
</a></li>
</ul>
</ul>
</div>
</div>
</nav>
</nav>
</header>
</header>
src/app/+shell/ui/components/header/feature/header.component.ts
View file @
5465e871
...
@@ -10,6 +10,12 @@ import { AuthService } from '../../../../../+login/data-access/Services/Auth.Ser
...
@@ -10,6 +10,12 @@ import { AuthService } from '../../../../../+login/data-access/Services/Auth.Ser
changeDetection
:
ChangeDetectionStrategy
.
OnPush
,
changeDetection
:
ChangeDetectionStrategy
.
OnPush
,
})
})
export
class
HeaderComponent
implements
OnInit
{
export
class
HeaderComponent
implements
OnInit
{
list
:
{
href
:
string
,
title
:
string
}[]
=
[
{
href
:
'home'
,
title
:
'Trang chủ'
},
{
href
:
'ListJob'
,
title
:
'Danh sách công việc'
},
{
href
:
'admin'
,
title
:
'Trang Admin'
}
];
ngOnInit
():
void
{}
ngOnInit
():
void
{}
constructor
(
private
authentication
:
AuthService
)
{}
constructor
(
private
authentication
:
AuthService
)
{}
logout
()
{
logout
()
{
...
...
src/app/+shell/ui/layout.component.ts
View file @
5465e871
...
@@ -33,7 +33,9 @@ import { NzLayoutModule } from 'ng-zorro-antd/layout';
...
@@ -33,7 +33,9 @@ import { NzLayoutModule } from 'ng-zorro-antd/layout';
<nz-layout>
<nz-layout>
<meu-header></meu-header>
<meu-header></meu-header>
<div class="tw-relative tw-min-h-[92dvh]">
<div class="tw-relative tw-min-h-[92dvh]">
<router-outlet></router-outlet>
<div class="tw-max-w-5xl tw-mx-auto tw-py-10 tw-px-3 tw-bg-white">
<router-outlet></router-outlet>
</div>
</div>
</div>
<meu-footer></meu-footer>
<meu-footer></meu-footer>
</nz-layout>
</nz-layout>
...
@@ -45,7 +47,7 @@ import { NzLayoutModule } from 'ng-zorro-antd/layout';
...
@@ -45,7 +47,7 @@ import { NzLayoutModule } from 'ng-zorro-antd/layout';
export
class
LayoutComponent
implements
OnInit
{
export
class
LayoutComponent
implements
OnInit
{
isSmallScreen
=
signal
(
false
);
isSmallScreen
=
signal
(
false
);
constructor
(
private
breakpointObserver
:
BreakpointObserver
)
{}
constructor
(
private
breakpointObserver
:
BreakpointObserver
)
{
}
ngOnInit
()
{
ngOnInit
()
{
this
.
breakpointObserver
.
observe
([
Breakpoints
.
Handset
]).
subscribe
((
res
)
=>
{
this
.
breakpointObserver
.
observe
([
Breakpoints
.
Handset
]).
subscribe
((
res
)
=>
{
this
.
isSmallScreen
.
set
(
res
.
matches
);
this
.
isSmallScreen
.
set
(
res
.
matches
);
...
...
src/app/app.component.ts
View file @
5465e871
...
@@ -12,3 +12,60 @@ import { RouterOutlet } from '@angular/router';
...
@@ -12,3 +12,60 @@ import { RouterOutlet } from '@angular/router';
export
class
AppComponent
{
export
class
AppComponent
{
title
=
'Meu-template-CSR'
;
title
=
'Meu-template-CSR'
;
}
}
// onOk(): void {
// if (this.isOnEdit && this.selectedJob) {
// if(!this.validateJobForm.valid){
// this.createNotification();
// return;}
// const updatedJob: JobModel = {
// id: this.selectedJob.id,
// type: this.validateJobForm.value.type ?? undefined,
// created_at: this.selectedJob.created_at,
// company: this.validateJobForm.value.company ?? undefined,
// location: this.validateJobForm.value.location ?? undefined,
// title: this.validateJobForm.value.title ?? undefined,
// description: this.validateJobForm.value.description ?? undefined,
// };
// this.jobService.editJob(updatedJob).pipe(
// tap((res) => {
// this.message.success(res.message, { nzDuration: 2500 });
// this._cdr.markForCheck();
// this.loadDataFromServer();
// }),
// catchError((err) => {
// this.message.error(err.message, { nzDuration: 2500 });
// return of(null);
// })
// ).subscribe();
// } else if (this.isOnAdd && this.selectedJob) {
// if(!this.validateJobForm.valid){
// this.createNotification();
// return;}
// const newJob: JobModel = {
// id: this.selectedJob.id,
// type: this.validateJobForm.value.type ?? undefined,
// created_at: this.selectedJob.created_at,
// company: this.validateJobForm.value.company ?? undefined,
// location: this.validateJobForm.value.location ?? undefined,
// title: this.validateJobForm.value.title ?? undefined,
// description: this.validateJobForm.value.description ?? undefined,
// };
// this.loading = true;
// this.jobService.addingNewJob(newJob).pipe(
// tap((res) => {
// this.message.success(res.message, { nzDuration: 2500 });
// this.loadDataFromServer();
// }),
// catchError((err) => {
// this.message.error(err.message, { nzDuration: 2500 });
// return of(null);
// })
// ).subscribe();
// }
// this.isOpenModal = false;
// this.isOnEdit = false;
// this.isOnAdd = false;
// }
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment