Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
Ariela Ramdania
siplah-fe
Commits
8ab028b4
Unverified
Commit
8ab028b4
authored
11 months ago
by
Ariela Ramdania
Committed by
GitHub
11 months ago
Browse files
Options
Download
Plain Diff
Merge branch 'main' into ariela
parents
6043ffa7
153c6749
main
ajeng
ariela
1 merge request
!62
update presensi dan setting
Changes
7
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
src/components/Sidebar.vue
+9
-3
src/components/Sidebar.vue
src/pages/ajuan/time-off.vue
+21
-15
src/pages/ajuan/time-off.vue
src/pages/index.vue
+93
-25
src/pages/index.vue
src/pages/kelola/overtime.vue
+3
-1
src/pages/kelola/overtime.vue
src/pages/kelola/time-off.vue
+73
-63
src/pages/kelola/time-off.vue
src/pages/report/okr.vue
+5
-1
src/pages/report/okr.vue
src/stores/api/ajuan/time-off.ts
+4
-19
src/stores/api/ajuan/time-off.ts
with
208 additions
and
127 deletions
+208
-127
src/components/Sidebar.vue
View file @
8ab028b4
...
...
@@ -62,7 +62,7 @@
<div
class=
"fw-bolder"
>
Daily Report
</div>
</router-link>
</li>
<li
class=
"menu-item"
:class=
"
{ active: $route.path === '/ajuan/overtime' }">
<li
v-if=
"isAuthorized('pengajuanLembur')"
class=
"menu-item"
:class=
"
{ active: $route.path === '/ajuan/overtime' }">
<router-link
to=
"/ajuan/overtime"
class=
"menu-link"
>
<i
class=
"menu-icon tf-icons bx bxs-hourglass"
></i>
<div>
Lembur
</div>
...
...
@@ -153,12 +153,18 @@ export default defineComponent({
dataProject
:
[
'
HRD
'
,
'
CTO
'
],
dataKaryawan
:
[
'
HRD
'
,
'
CTO
'
],
dataKeyResult
:
[
'
HRD
'
],
presensi
:
[
'
HRD
'
,
'
PM
'
,
'
Frontend
'
,
'
Backend
'
,
'
UI/UX
'
],
dailyReport
:
[
'
HRD
'
,
'
Frontend
'
,
'
Backend
'
,
'
UI/UX
'
],
pengajuanLembur
:
[
'
HRD
'
,
'
UIUX
'
,
'
Frontend
'
,
'
PM
'
],
presensi
:
[
'
UIUX
'
,
'
HRD
'
,
'
PM
'
,
'
Frontend
'
],
dailyReport
:
[
'
UIUX
'
,
'
HRD
'
,
'
Frontend
'
],
readpresensi
:
[
'
HRD
'
,
'
PM
'
,
'
CTO
'
],
readdailyReport
:
[
'
HRD
'
,
'
PM
'
,
'
CTO
'
],
lembur
:
[
'
HRD
'
,
'
CTO
'
,
'
Frontend
'
],
cuti
:
[
'
HRD
'
,
'
CTO
'
,
'
Frontend
'
],
lembur
:
[
'
HRD
'
,
'
CTO
'
,
'
PM
'
],
cuti
:
[
'
HRD
'
,
'
CTO
'
,
'
PM
'
],
kelolaAbsensi
:
[
'
HRD
'
,
'
CTO
'
,
'
PM
'
],
okr
:
[
'
HRD
'
,
'
PM
'
,
'
CTO
'
],
meeting
:
[
'
HRD
'
,
'
PM
'
],
...
...
This diff is collapsed.
Click to expand it.
src/pages/ajuan/time-off.vue
View file @
8ab028b4
...
...
@@ -69,14 +69,14 @@ const formatTanggal = (tanggal: string) => {
const
openModal
=
(
mode
:
'
add
'
|
'
edit
'
,
index
:
number
=
-
1
)
=>
{
if
(
mode
===
'
edit
'
)
{
if
(
listTimeOff
.
value
[
index
].
details
[
0
].
status
!==
'
rejected
'
)
{
if
(
listTimeOff
.
value
[
index
].
status
!==
'
rejected
'
)
{
return
;
}
}
formMode
.
value
=
mode
;
if
(
mode
===
'
edit
'
)
{
editedIndex
.
value
=
index
;
const
selectedItem
=
listTimeOff
.
value
[
index
]
.
details
[
0
]
;
const
selectedItem
=
listTimeOff
.
value
[
index
];
formItem
.
value
=
{
start_date
:
formatISO
(
parseISO
(
selectedItem
.
start_date
),
{
representation
:
'
date
'
}),
end_date
:
formatISO
(
parseISO
(
selectedItem
.
end_date
),
{
representation
:
'
date
'
}),
...
...
@@ -151,7 +151,7 @@ const saveData = async () => {
if
(
formMode
.
value
===
'
add
'
)
{
await
apiTimeOffStore
.
postTimeOff
(
formItem
.
value
);
}
else
if
(
formMode
.
value
===
'
edit
'
)
{
const
id
=
listTimeOff
.
value
[
editedIndex
.
value
].
details
[
0
].
id_time_off
;
const
id
=
listTimeOff
.
value
[
editedIndex
.
value
].
id_time_off
;
await
apiTimeOffStore
.
putTimeOff
(
formItem
.
value
,
id
);
}
getData
();
...
...
@@ -189,7 +189,7 @@ const openDeleteModal = (index: number) => {
};
const
deleteData
=
async
()
=>
{
const
id
=
listTimeOff
.
value
[
deletedIndex
.
value
].
details
[
0
].
id_time_off
;
const
id
=
listTimeOff
.
value
[
deletedIndex
.
value
].
id_time_off
;
await
apiTimeOffStore
.
deleteTimeOff
(
id
);
getData
();
};
...
...
@@ -219,7 +219,13 @@ const isImage = (url: string) => {
<div
class=
"col-md-4 d-flex justify-content-start align-items-center"
>
<div
class=
"input-group"
>
<span
class=
"input-group-text"
><i
class=
"bx bx-calendar"
></i></span>
<input
type=
"month"
class=
"form-control"
v-model=
"searchMonthYear"
placeholder=
"Pilih Bulan dan Tahun"
@
input=
"getData"
/>
<input
type=
"month"
class=
"form-control"
v-model=
"searchMonthYear"
placeholder=
"Pilih Bulan dan Tahun"
@
input=
"getData"
/>
</div>
</div>
<div
class=
"col-md-8 d-flex justify-content-end align-items-center"
>
...
...
@@ -257,18 +263,18 @@ const isImage = (url: string) => {
</tr>
<tr
v-else
v-for=
"(item, index) in listTimeOff"
:key=
"index"
>
<td>
{{
index
+
1
}}
</td>
<td>
{{
formatTanggal
(
item
.
details
[
0
].
start_date
)
}}
</td>
<td>
{{
formatTanggal
(
item
.
details
[
0
].
end_date
)
}}
</td>
<td>
{{
item
.
details
[
0
].
type
}}
</td>
<td>
{{
formatTanggal
(
item
.
start_date
)
}}
</td>
<td>
{{
formatTanggal
(
item
.
end_date
)
}}
</td>
<td>
{{
item
.
type
}}
</td>
<td>
<span
:class=
"
{
'badge bg-label-warning': item.
details[0].
status === 'pending',
'badge bg-label-success': item.
details[0].
status === 'approved',
'badge bg-label-danger': item.
details[0].
status === 'rejected'
'badge bg-label-warning': item.status === 'pending',
'badge bg-label-success': item.status === 'approved',
'badge bg-label-danger': item.status === 'rejected'
}"
>
{{
item
.
details
[
0
].
status
}}
{{
item
.
status
}}
</span>
</td>
<td>
...
...
@@ -278,11 +284,11 @@ const isImage = (url: string) => {
role=
"button"
data-bs-toggle=
"modal"
data-bs-target=
"#viewModal"
@
click=
"openView(item
.details[0]
)"
@
click=
"openView(item)"
><i
class=
"bx bx-show-alt me-1"
></i>
View
</span
>
<span
v-if=
"item.
details[0].
status === 'rejected'"
v-if=
"item.status === 'rejected'"
class=
"badge bg-label-warning me-1"
role=
"button"
@
click=
"openModal('edit', index)"
...
...
@@ -291,7 +297,7 @@ const isImage = (url: string) => {
><i
class=
"bx bx-edit-alt me-1"
></i>
Edit
</span>
<span
v-if=
"item.
details[0].
status !== 'approved'"
v-if=
"item.status !== 'approved'"
class=
"badge bg-label-danger me-1"
role=
"button"
data-bs-toggle=
"modal"
...
...
This diff is collapsed.
Click to expand it.
src/pages/index.vue
View file @
8ab028b4
<route
lang=
"yaml"
>
meta:
layout: default
requiresAuth: true
</route>
meta:
layout: default
requiresAuth: true
</route>
<
script
setup
lang=
"ts"
>
import
{
computed
,
onMounted
,
ref
}
from
'
vue
'
;
import
{
useAuthStore
}
from
'
../stores/api/authStore
'
;
import
{
useApiTimeOffStore
}
from
'
@/stores/api/ajuan/time-off
'
;
import
{
useApiOvertimeStrore
}
from
'
@/stores/api/ajuan/overtime
'
;
import
{
useApiMeetingStore
}
from
'
@/stores/api/meeting/meeting
'
;
import
{
useApiEmployeeStore
}
from
'
@/stores/api/master/karyawan
'
;
import
{
storeToRefs
}
from
'
pinia
'
;
import
{
parseISO
,
format
,
isValid
}
from
'
date-fns
'
;
import
{
id
}
from
'
date-fns/locale
'
;
const
paramsMeeting
=
ref
({
page
:
1
,
pageSize
:
10
});
const
paramsTimeOff
=
ref
({
page
:
1
,
pageSize
:
10
});
const
auth
=
useAuthStore
();
const
lembur
=
useApiOvertimeStrore
();
const
employeeName
=
computed
(()
=>
auth
.
employee
?.
name
);
const
totalLembur
=
computed
(()
=>
lembur
.
totalData
);
const
apiMeetingStore
=
useApiMeetingStore
();
const
{
listMeeting
}
=
storeToRefs
(
apiMeetingStore
);
const
apiTimeOffStore
=
useApiTimeOffStore
();
const
{
listTimeOff
,
totalData
}
=
storeToRefs
(
apiTimeOffStore
);
const
apiEmployeeStore
=
useApiEmployeeStore
();
const
{
listEmployee
}
=
storeToRefs
(
apiEmployeeStore
);
const
getData
=
async
()
=>
{
await
apiMeetingStore
.
getMeeting
({
...
paramsMeeting
.
value
});
await
apiTimeOffStore
.
getTimeOff
({
...
paramsTimeOff
.
value
});
await
apiEmployeeStore
.
getEmployee
();
};
onMounted
(()
=>
{
getData
();
});
const
formatTanggal
=
(
tanggal
:
string
)
=>
{
const
date
=
parseISO
(
tanggal
);
if
(
!
isValid
(
date
))
{
return
'
Invalid Date
'
;
}
return
format
(
date
,
'
dd MMMM yyyy
'
,
{
locale
:
id
});
};
const
getEmployeeName
=
(
id_employee
:
string
)
=>
{
const
employee
=
listEmployee
.
value
.
find
((
employee
:
{
id_employee
:
string
})
=>
employee
.
id_employee
===
id_employee
);
return
employee
?
employee
.
name
:
'
Unknown
'
;
};
</
script
>
<
template
>
<div
class=
"container-xxl flex-grow-1 container-p-y"
>
<div
class=
"row"
>
<div
class=
"col-lg-8 mb-4 order-0"
>
<div
:class=
"
auth.employee?.jabatan === 'HRD' || auth.employee?.jabatan === 'PM' || auth.employee?.jabatan === 'CTO'
? 'col-lg-12 mb-4 order-0'
: 'col-lg-8 mb-4 order-0'
"
>
<div
class=
"card"
>
<div
class=
"d-flex align-items-end row"
>
<div
class=
"col-sm-7"
>
<div
class=
"card-body"
>
<h5
class=
"card-title text-primary"
>
Hai
Admin
! 👋
</h5>
<h5
class=
"card-title text-primary"
>
Hai
{{
employeeName
}}
! 👋
</h5>
<p
class=
"mb-4"
>
Waktunya untuk memulai hari dengan semangat! Ayo mulai hari ini dengan penuh energi dan produktivitas.
</p>
<!-- Tombol untuk melakukan presensi -->
<router-link
to=
"/absensi/absensi"
>
<button
type=
"button"
class=
"btn btn-primary"
>
Presensi Sekarang
</button>
</router-link>
...
...
@@ -36,16 +90,20 @@
</div>
</div>
<div
class=
"col-4 mb-4"
>
<div
class=
"card"
>
<div
class=
"card-body"
>
<div
class=
"d-flex flex-column align-items-center justify-content-center text-center"
>
<div
class=
"card-title"
>
<h5
class=
"text-nowrap text-primary mb-2"
>
Congratulations Admin! 🎉
</h5>
<span
class=
"badge bg-label-warning rounded-pill"
>
Mei 2024
</span>
</div>
<div
class=
"mt-auto"
>
<h5
class=
"mb-0 display-1"
>
92
</h5>
<div
v-if=
"auth.employee?.jabatan !== 'HRD' && auth.employee?.jabatan !== 'PM' && auth.employee?.jabatan !== 'CTO'"
>
<div
class=
"col-4 mb-4"
>
<div
class=
"card"
>
<div
class=
"card-body"
>
<div
class=
"d-flex flex-column align-items-center justify-content-center text-center"
>
<div
class=
"card-title"
>
<h5
class=
"text-nowrap text-primary mb-2"
>
Congratulations
{{
employeeName
}}
! 🎉
</h5>
<span
class=
"badge bg-label-warning rounded-pill"
>
Juni 2024
</span>
</div>
<div
class=
"mt-auto"
>
<h5
class=
"mb-0 display-1"
>
98
</h5>
</div>
</div>
</div>
</div>
...
...
@@ -61,7 +119,7 @@
<i
class=
"menu-icon tf-icons bx bx-user me-4"
style=
"font-size: 2rem; color: green"
></i>
<div>
<span
class=
"fw-semibold d-block mb-1"
>
Karyawan
</span>
<h3
class=
"card-title fw-bold mb-2"
>
50
</h3>
<h3
class=
"card-title fw-bold mb-2"
>
8
</h3>
</div>
</div>
</div>
...
...
@@ -74,7 +132,7 @@
<i
class=
"menu-icon tf-icons bx bxl-codepen me-4"
style=
"font-size: 2rem; color: orange"
></i>
<div>
<span
class=
"fw-semibold d-block mb-1"
>
Jabatan
</span>
<h3
class=
"card-title fw-bold mb-2"
>
1
5
</h3>
<h3
class=
"card-title fw-bold mb-2"
>
5
</h3>
</div>
</div>
</div>
...
...
@@ -87,7 +145,7 @@
<i
class=
"menu-icon tf-icons bx bx-task me-4"
style=
"font-size: 2rem; color: red"
></i>
<div>
<span
class=
"fw-semibold d-block mb-1"
>
Project
</span>
<h3
class=
"card-title fw-bold mb-2"
>
10
</h3>
<h3
class=
"card-title fw-bold mb-2"
>
4
</h3>
</div>
</div>
</div>
...
...
@@ -100,7 +158,7 @@
<i
class=
"menu-icon tf-icons bx bx-time me-4"
style=
"font-size: 2rem; color: black"
></i>
<div>
<span
class=
"fw-semibold d-block mb-1"
>
Presensi
</span>
<h3
class=
"card-title fw-bold mb-2"
>
3
0
</h3>
<h3
class=
"card-title fw-bold mb-2"
>
1
0
</h3>
</div>
</div>
</div>
...
...
@@ -113,7 +171,7 @@
<i
class=
"menu-icon tf-icons bx bx-sun me-4"
style=
"font-size: 2rem; color: yellow"
></i>
<div>
<span
class=
"fw-semibold d-block mb-1"
>
Cuti
</span>
<h3
class=
"card-title fw-bold mb-2"
>
2
</h3>
<h3
class=
"card-title fw-bold mb-2"
>
{{
totalData
}}
</h3>
</div>
</div>
</div>
...
...
@@ -126,7 +184,7 @@
<i
class=
"menu-icon tf-icons bx bxs-hourglass me-4"
style=
"font-size: 2rem; color: green"
></i>
<div>
<span
class=
"fw-semibold d-block mb-1"
>
Lembur
</span>
<h3
class=
"card-title fw-bold mb-2"
>
0
</h3>
<h3
class=
"card-title fw-bold mb-2"
>
{{
totalLembur
}}
</h3>
</div>
</div>
</div>
...
...
@@ -148,9 +206,14 @@
</tr>
</thead>
<tbody
class=
"table-border-bottom-0"
>
<tr>
<tr
v-if=
"listMeeting.length === 0"
>
<td
colspan=
"2"
>
Tidak ada meeting hari ini
</td>
</tr>
<tr
v-else
v-for=
"(item, index) in listMeeting"
:key=
"index"
>
<td>
{{
formatTanggal
(
item
.
date
)
}}
</td>
<td>
{{
item
.
description
}}
</td>
</tr>
</tbody>
</table>
</div>
...
...
@@ -168,9 +231,14 @@
</tr>
</thead>
<tbody
class=
"table-border-bottom-0"
>
<tr>
<tr
v-if=
"listTimeOff.length === 0"
>
<td
colspan=
"2"
>
Tidak ada yang Cuti selama 7 hari kedepan
</td>
</tr>
<tr
v-else
v-for=
"(item, index) in listTimeOff"
:key=
"index"
>
<td>
{{
getEmployeeName
(
item
.
id_employee
)
}}
</td>
<td>
{{
formatTanggal
(
item
.
start_date
)
}}
-
{{
formatTanggal
(
item
.
end_date
)
}}
</td>
</tr>
</tbody>
</table>
</div>
...
...
This diff is collapsed.
Click to expand it.
src/pages/kelola/overtime.vue
View file @
8ab028b4
...
...
@@ -11,6 +11,7 @@ import Pagination from '@/components/pagination/Pagination2.vue';
import
{
useApiOvertimeStrore
}
from
'
@/stores/api/ajuan/overtime
'
;
import
{
useApiEmployeeStore
}
from
'
@/stores/api/master/karyawan
'
;
import
{
storeToRefs
}
from
'
pinia
'
;
import
{
useAuthStore
}
from
'
@/stores/api/authStore
'
;
const
searchMonthYear
=
ref
(
''
);
const
searchQuery
=
ref
(
''
);
...
...
@@ -24,6 +25,7 @@ const apiOvertimeStore = useApiOvertimeStrore();
const
{
listOvertime
,
totalData
}
=
storeToRefs
(
apiOvertimeStore
);
const
apiEmployeeStore
=
useApiEmployeeStore
();
const
{
listEmployee
}
=
storeToRefs
(
apiEmployeeStore
);
const
auth
=
useAuthStore
();
const
getData
=
async
()
=>
{
await
apiOvertimeStore
.
getOvertime
({
...
paramsOvertime
.
value
,
q
:
searchQuery
.
value
,
date
:
searchMonthYear
.
value
});
...
...
@@ -359,7 +361,7 @@ const fetchAttachment = async () => {
</div>
</div>
<div
class=
"modal-footer justify-content-between"
>
<div>
<div
v-if=
"auth.employee?.jabatan !== 'PM'&& auth.employee?.jabatan !== 'CTO'"
>
<button
v-if=
"viewItem.status === 'pending'"
type=
"button"
...
...
This diff is collapsed.
Click to expand it.
src/pages/kelola/time-off.vue
View file @
8ab028b4
<route
lang=
"yaml"
>
meta:
layout: default
requiresAuth: true
</route>
meta:
layout: default
requiresAuth: true
</route>
<
script
setup
lang=
"ts"
>
import
{
ref
,
onMounted
,
computed
}
from
'
vue
'
;
import
{
format
,
isValid
,
parseISO
}
from
'
date-fns
'
;
...
...
@@ -11,19 +11,18 @@ import Pagination from '@/components/pagination/Pagination2.vue';
import
{
useApiTimeOffStore
}
from
'
@/stores/api/ajuan/time-off
'
;
import
{
useApiEmployeeStore
}
from
'
@/stores/api/master/karyawan
'
;
import
{
storeToRefs
}
from
'
pinia
'
;
import
{
useAuthStore
}
from
'
@/stores/api/authStore
'
;
interface
TimeOff
{
details
:
{
id_employee
:
string
;
id_time_off
:
string
;
start_date
:
string
;
end_date
:
string
;
type
:
string
;
attachment
:
string
;
status
:
string
;
description
:
string
;
}[];
jumlah_cuti
:
string
;
id_employee
:
string
;
id_time_off
:
string
;
start_date
:
string
;
end_date
:
string
;
type
:
string
;
attachment
:
string
;
status
:
string
;
description
:
string
;
jumlah_cuti
:
number
;
}
const
selectedItem
=
ref
<
TimeOff
|
null
>
(
null
);
...
...
@@ -38,6 +37,7 @@ const apiTimeOffStore = useApiTimeOffStore();
const
{
listTimeOff
,
totalData
}
=
storeToRefs
(
apiTimeOffStore
);
const
apiEmployeeStore
=
useApiEmployeeStore
();
const
{
listEmployee
}
=
storeToRefs
(
apiEmployeeStore
);
const
auth
=
useAuthStore
();
const
getData
=
async
()
=>
{
await
apiTimeOffStore
.
getTimeOff
({
...
paramsTimeOff
.
value
,
q
:
searchQuery
.
value
,
date
:
searchMonthYear
.
value
});
...
...
@@ -49,7 +49,7 @@ onMounted(() => {
});
const
getEmployeeName
=
(
id_employee
:
string
)
=>
{
const
employee
=
listEmployee
.
value
.
find
((
employee
:
{
id_employee
:
string
;
})
=>
employee
.
id_employee
===
id_employee
);
const
employee
=
listEmployee
.
value
.
find
((
employee
:
{
id_employee
:
string
})
=>
employee
.
id_employee
===
id_employee
);
return
employee
?
employee
.
name
:
'
Unknown
'
;
};
...
...
@@ -68,7 +68,7 @@ const openModal = (item: TimeOff | null, type: string) => {
const
updateStatus
=
async
()
=>
{
if
(
selectedItem
.
value
&&
actionType
.
value
)
{
const
id
=
selectedItem
.
value
.
details
[
0
].
id_time_off
;
const
id
=
selectedItem
.
value
.
id_time_off
;
const
descriptionValue
=
description
.
value
;
try
{
...
...
@@ -79,13 +79,13 @@ const updateStatus = async () => {
}
const
newStatus
=
actionType
.
value
===
'
approve
'
?
'
approved
'
:
'
rejected
'
;
const
index
=
listTimeOff
.
value
[
0
].
details
.
findIndex
((
item
:
{
id_time_off
:
string
;
})
=>
item
.
id_time_off
===
id
);
const
index
=
listTimeOff
.
value
.
findIndex
((
item
:
{
id_time_off
:
string
})
=>
item
.
id_time_off
===
id
);
if
(
index
!==
-
1
)
{
listTimeOff
.
value
[
index
].
details
[
0
].
status
=
newStatus
;
listTimeOff
.
value
[
index
].
status
=
newStatus
;
if
(
newStatus
===
'
rejected
'
)
{
listTimeOff
.
value
[
index
].
details
[
0
].
description
=
'
Alasan penolakan:
'
+
descriptionValue
;
listTimeOff
.
value
[
index
].
description
=
'
Alasan penolakan:
'
+
descriptionValue
;
}
else
{
listTimeOff
.
value
[
index
].
details
[
0
].
description
=
''
;
listTimeOff
.
value
[
index
].
description
=
''
;
}
}
description
.
value
=
''
;
...
...
@@ -96,17 +96,15 @@ const updateStatus = async () => {
};
const
viewItem
=
ref
<
TimeOff
>
({
details
:
[{
id_employee
:
''
,
id_time_off
:
''
,
start_date
:
''
,
end_date
:
''
,
type
:
''
,
attachment
:
''
,
status
:
''
,
description
:
''
}],
jumlah_cuti
:
''
id_employee
:
''
,
id_time_off
:
''
,
start_date
:
''
,
end_date
:
''
,
type
:
''
,
attachment
:
''
,
status
:
''
,
description
:
''
,
jumlah_cuti
:
0
,
});
const
openView
=
(
item
:
TimeOff
)
=>
{
...
...
@@ -130,7 +128,6 @@ const fetchAttachment = async () => {
console
.
error
(
'
Error fetching attachment:
'
,
error
);
}
};
</
script
>
<
template
>
...
...
@@ -140,13 +137,25 @@ const fetchAttachment = async () => {
<div
class=
"col-md-3 d-flex justify-content-start align-items-center"
>
<div
class=
"input-group"
>
<span
class=
"input-group-text"
><i
class=
"bx bx-search-alt"
></i></span>
<input
type=
"text"
class=
"form-control"
v-model=
"searchQuery"
placeholder=
"Search Karyawan..."
@
input=
"getData"
/>
<input
type=
"text"
class=
"form-control"
v-model=
"searchQuery"
placeholder=
"Search Karyawan..."
@
input=
"getData"
/>
</div>
</div>
<div
class=
"col-md-3 d-flex justify-content-start align-items-center"
>
<div
class=
"input-group"
>
<span
class=
"input-group-text"
><i
class=
"bx bx-calendar"
></i></span>
<input
type=
"month"
class=
"form-control"
v-model=
"searchMonthYear"
placeholder=
"Pilih Bulan dan Tahun"
@
input=
"getData"
/>
<input
type=
"month"
class=
"form-control"
v-model=
"searchMonthYear"
placeholder=
"Pilih Bulan dan Tahun"
@
input=
"getData"
/>
</div>
</div>
</div>
...
...
@@ -171,18 +180,18 @@ const fetchAttachment = async () => {
</tr>
<tr
v-else
v-for=
"(item, index) in listTimeOff"
:key=
"index"
>
<td>
{{
index
+
1
}}
</td>
<td>
{{
getEmployeeName
(
item
.
details
[
0
].
id_employee
)
}}
</td>
<td>
{{
formatTanggal
(
item
.
details
[
0
].
start_date
)
}}
</td>
<td>
{{
item
.
details
[
0
].
type
}}
</td>
<td>
{{
getEmployeeName
(
item
.
id_employee
)
}}
</td>
<td>
{{
formatTanggal
(
item
.
start_date
)
}}
</td>
<td>
{{
item
.
type
}}
</td>
<td>
<span
:class=
"
{
'badge bg-label-warning': item.
details[0].
status === 'pending',
'badge bg-label-success': item.
details[0].
status === 'approved',
'badge bg-label-danger': item.
details[0].
status === 'rejected'
'badge bg-label-warning': item.status === 'pending',
'badge bg-label-success': item.status === 'approved',
'badge bg-label-danger': item.status === 'rejected'
}"
>
{{
item
.
details
[
0
].
status
}}
{{
item
.
status
}}
</span>
</td>
<td>
...
...
@@ -220,7 +229,8 @@ const fetchAttachment = async () => {
<div
class=
"modal-body"
>
<p>
Apakah Anda yakin ingin
{{
actionType
===
'
approve
'
?
'
menyetujui
'
:
'
menolak
'
}}
pengajuan cuti oleh
<b>
{{
getEmployeeName
(
selectedItem
?.
details
[
0
].
id_employee
??
''
)
}}
</b>
pada tanggal
<b>
{{
formatTanggal
(
selectedItem
?.
details
[
0
].
start_date
??
''
)
}}
</b>
<b>
{{
getEmployeeName
(
selectedItem
?.
id_employee
??
''
)
}}
</b>
pada tanggal
<b>
{{
formatTanggal
(
selectedItem
?.
start_date
??
''
)
}}
</b>
</p>
<!-- Input alasan penolakan -->
<div
v-if=
"actionType === 'reject'"
class=
"mb-3"
>
...
...
@@ -255,7 +265,7 @@ const fetchAttachment = async () => {
type=
"text"
id=
"id_employee"
class=
"form-control"
:value=
"getEmployeeName(viewItem.
details[0].
id_employee)"
:value=
"getEmployeeName(viewItem.id_employee)"
disabled
/>
</div>
...
...
@@ -267,7 +277,7 @@ const fetchAttachment = async () => {
type=
"text"
id=
"start_date"
class=
"form-control"
:value=
"formatTanggal(viewItem.
details[0].
start_date)"
:value=
"formatTanggal(viewItem.start_date)"
placeholder=
"DD / MM / YY"
disabled
/>
...
...
@@ -280,7 +290,7 @@ const fetchAttachment = async () => {
type=
"text"
id=
"end_date"
class=
"form-control"
:value=
"formatTanggal(viewItem.
details[0].
end_date)"
:value=
"formatTanggal(viewItem.end_date)"
placeholder=
"DD / MM / YY"
disabled
/>
...
...
@@ -293,7 +303,7 @@ const fetchAttachment = async () => {
type=
"text"
id=
"type"
class=
"form-control"
v-model=
"viewItem.
details[0].
type"
v-model=
"viewItem.type"
placeholder=
"DD / MM / YY"
disabled
/>
...
...
@@ -308,10 +318,10 @@ const fetchAttachment = async () => {
<div
class=
"row g-2"
>
<div
class=
"col mt-3"
>
<label
for=
"attachment"
class=
"form-label mb-2"
>
Attachment
</label>
<div
v-if=
"viewItem.
details[0].
attachment"
>
<template
v-if=
"isImage(viewItem.
details[0].
attachment)"
>
<div
v-if=
"viewItem.attachment"
>
<template
v-if=
"isImage(viewItem.attachment)"
>
<img
:src=
"viewItem.
details[0].
attachment"
:src=
"viewItem.attachment"
alt=
"Attachment Preview"
style=
"max-width: 100%; max-height: 400px"
/>
...
...
@@ -328,34 +338,34 @@ const fetchAttachment = async () => {
<label
for=
"status"
class=
"form-label"
>
Status
</label>
<div>
<span
:class=
"{
'badge bg-label-warning': viewItem.
details[0].
status === 'pending',
'badge bg-label-success': viewItem.
details[0].
status === 'approved',
'badge bg-label-danger': viewItem.
details[0].
status === 'rejected'
}"
>
{{ viewItem.
details[0].
status }}
</span>
:class=
"{
'badge bg-label-warning': viewItem.status === 'pending',
'badge bg-label-success': viewItem.status === 'approved',
'badge bg-label-danger': viewItem.status === 'rejected'
}"
>
{{ viewItem.status }}
</span>
</div>
</div>
</div>
<div
v-if=
"viewItem.
details[0].
status === 'rejected'"
class=
"row"
>
<div
v-if=
"viewItem.status === 'rejected'"
class=
"row"
>
<div
class=
"col mt-3"
>
<label
for=
"description"
class=
"form-label"
>
Deskripsi
</label>
<textarea
class=
"form-control"
id=
"description"
rows=
"3"
v-model=
"viewItem.
details[0].
description"
v-model=
"viewItem.description"
disabled
></textarea>
</div>
</div>
</div>
<div
class=
"modal-footer justify-content-between"
>
<div>
<div
v-if=
"auth.employee?.jabatan !== 'PM' && auth.employee?.jabatan !== 'CTO'"
>
<button
v-if=
"viewItem.
details[0].
status === 'pending'"
v-if=
"viewItem.status === 'pending'"
type=
"button"
class=
"btn btn-success me-2"
@
click=
"openModal(viewItem, 'approve')"
...
...
@@ -366,7 +376,7 @@ const fetchAttachment = async () => {
Setujui
</button>
<button
v-if=
"viewItem.
details[0].
status === 'pending'"
v-if=
"viewItem.status === 'pending'"
type=
"button"
class=
"btn btn-danger"
@
click=
"openModal(viewItem, 'reject')"
...
...
This diff is collapsed.
Click to expand it.
src/pages/report/okr.vue
View file @
8ab028b4
...
...
@@ -7,6 +7,7 @@ import { useApiEmployeeStore } from '@/stores/api/master/karyawan';
import
{
useApiKeyResultStore
}
from
'
@/stores/api/master/keyResult
'
;
import
{
storeToRefs
}
from
'
pinia
'
;
import
{
formatTanggal
,
getAvgTarget
,
getKeyResultName
,
formatType
,
getKeyResultTarget
,
formatDateForInput
}
from
'
./helper/okr
'
import
{
useAuthStore
}
from
'
@/stores/api/authStore
'
;
const
searchQuery
=
ref
(
''
);
const
searchMonthYear
=
ref
(
''
);
...
...
@@ -25,6 +26,7 @@ const apiEmployeeStore = useApiEmployeeStore();
const
{
selectedEmployee
}
=
storeToRefs
(
apiEmployeeStore
);
const
apiKeyResultStore
=
useApiKeyResultStore
();
const
{
listKeyResult
,
selectKeyResult
}
=
storeToRefs
(
apiKeyResultStore
);
const
auth
=
useAuthStore
();
const
getData
=
async
()
=>
{
await
apiAssessmentStore
.
getAssessment
({
...
paramsAssessment
.
value
,
q
:
searchQuery
.
value
,
date
:
searchMonthYear
.
value
});
...
...
@@ -218,7 +220,7 @@ const deleteData = async () => {
<input
type=
"month"
class=
"form-control"
v-model=
"searchMonthYear"
placeholder=
"Pilih Bulan dan Tahun"
@
input=
"getData"
/>
</div>
</div>
<div
class=
"col-md-6 d-flex justify-content-end align-items-center"
>
<div
v-if=
"auth.employee?.jabatan !== 'CTO'"
class=
"col-md-6 d-flex justify-content-end align-items-center"
>
<button
class=
"btn btn-primary"
type=
"button"
...
...
@@ -271,6 +273,7 @@ const deleteData = async () => {
><i
class=
"bx bx-show-alt me-1"
></i>
View
</span
>
<span
v-if=
"auth.employee?.jabatan !== 'CTO'"
class=
"badge bg-label-warning me-1"
role=
"button"
@
click=
"openModal('edit', index)"
...
...
@@ -279,6 +282,7 @@ const deleteData = async () => {
><i
class=
"bx bx-edit-alt me-1"
></i>
Edit
</span>
<span
v-if=
"auth.employee?.jabatan !== 'CTO'"
class=
"badge bg-label-danger me-1"
role=
"button"
data-bs-toggle=
"modal"
...
...
This diff is collapsed.
Click to expand it.
src/stores/api/ajuan/time-off.ts
View file @
8ab028b4
...
...
@@ -3,42 +3,27 @@ import { defineStore } from "pinia";
import
httpClient
from
"
@/services/ts/httpClient
"
;
interface
TimeOff
{
id_employee
:
string
;
id_time_off
:
string
;
id_employee
:
string
;
start_date
:
string
;
end_date
:
string
;
type
:
string
;
attachment
:
string
;
status
:
string
;
description
:
string
;
}
interface
TransformedTimeOff
{
details
:
TimeOff
[];
jumlah_cuti
:
string
;
jumlah_cuti
:
number
;
}
export
const
useApiTimeOffStore
=
defineStore
(
'
api-time-off
'
,
()
=>
{
const
listTimeOff
=
ref
<
Transformed
TimeOff
[]
>
([]);
const
listTimeOff
=
ref
<
TimeOff
[]
>
([]);
const
detailTimeOff
=
ref
<
TimeOff
|
null
>
(
null
);
const
timeOffAttachment
=
ref
(
''
);
const
totalData
=
ref
(
0
);
const
transformData
=
(
data
:
any
):
TransformedTimeOff
[]
=>
{
return
data
.
map
((
entry
:
any
)
=>
{
return
{
details
:
Object
.
keys
(
entry
)
.
filter
(
key
=>
!
isNaN
(
Number
(
key
)))
.
map
(
key
=>
entry
[
key
]),
jumlah_cuti
:
entry
.
jumlah_cuti
};
});
};
const
getTimeOff
=
async
(
params
:
any
)
=>
{
try
{
const
res
=
await
httpClient
.
query
(
'
/time-off
'
,
params
);
listTimeOff
.
value
=
transformData
(
res
.
data
.
data
)
;
listTimeOff
.
value
=
res
.
data
.
data
;
totalData
.
value
=
res
.
data
.
totalData
;
}
catch
(
error
)
{
throw
error
;
...
...
This diff is collapsed.
Click to expand it.
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
Menu
Projects
Groups
Snippets
Help