Skip to content

Commit

Permalink
Merge pull request #2288 from frappe/version-15-hotfix
Browse files Browse the repository at this point in the history
  • Loading branch information
krantheman authored Oct 16, 2024
2 parents eaeecd1 + 5f3220a commit 26dc3fe
Show file tree
Hide file tree
Showing 70 changed files with 2,319 additions and 438 deletions.
4 changes: 2 additions & 2 deletions .github/ISSUE_TEMPLATE/feature_request.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ body:
Welcome to Frappe HR issue tracker! Before submitting a request, please consider the following:
1. This tracker should only be used to report bugs and request features / enhancements to Frappe HR
- For questions and general support, checkout the [documentation](https://frappehr.com/docs) or use the [forum](https://discuss.frappe.io) to get inputs from the open source community.
- For questions and general support, checkout the [documentation](https://docs.frappe.io/hr) or use the [forum](https://discuss.frappe.io) to get inputs from the open source community.
2. Use the search function before creating a new issue. Duplicates will be closed and directed to
the original discussion.
3. When making a feature request, make sure to be as verbose as possible. The better you convey your message, the greater the drive to make it happen.
Expand Down Expand Up @@ -47,4 +47,4 @@ body:
attributes:
label: Additional context
description: Add any other context or screenshots about the feature request here.
...
...
2 changes: 1 addition & 1 deletion .github/helper/documentation.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ def docs_link_exists(body):
parts = parsed_url.path.split("/")
if len(parts) == 5 and parts[1] == "frappe" and parts[2] == "hrms":
return True
elif parsed_url.netloc == "frappehr.com":
elif parsed_url.netloc == "docs.frappe.io":
return True


Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ Frappe HR has everything you need to drive excellence within the company. It's a

## Learning and Community

1. [Documentation](https://frappehr.com/docs) - Extensive documentation for Frappe HR.
1. [Documentation](https://docs.frappe.io/hr) - Extensive documentation for Frappe HR.
2. [User Forum](https://discuss.erpnext.com/) - Engage with the community of ERPNext users and service providers.
3. [Telegram Group](https://t.me/frappehr) - Get instant help from the community of users.

Expand Down
122 changes: 122 additions & 0 deletions frontend/src/components/AttendanceCalendar.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
<template>
<div class="flex flex-col w-full gap-5" v-if="calendarEvents.data">
<div class="text-lg text-gray-800 font-bold">Attendance Calendar</div>

<div class="flex flex-col gap-6 bg-white py-6 px-3.5 rounded-lg border-none">
<!-- Month Change -->
<div class="flex flex-row justify-between items-center px-4">
<Button
icon="chevron-left"
variant="ghost"
@click="firstOfMonth = firstOfMonth.subtract(1, 'M')"
/>
<span class="text-lg text-gray-800 font-bold">
{{ firstOfMonth.format("MMMM") }} {{ firstOfMonth.format("YYYY") }}
</span>
<Button
icon="chevron-right"
variant="ghost"
@click="firstOfMonth = firstOfMonth.add(1, 'M')"
/>
</div>

<!-- Calendar -->
<div class="grid grid-cols-7 gap-y-3">
<div
v-for="day in DAYS"
class="flex justify-center text-gray-600 text-sm font-medium leading-6"
>
{{ day }}
</div>
<div v-for="_ in firstOfMonth.get('d')" />
<div v-for="index in firstOfMonth.endOf('M').get('D')">
<div
class="h-8 w-8 flex rounded-full mx-auto"
:class="getEventOnDate(index) && `bg-${colorMap[getEventOnDate(index)]}`"
>
<span class="text-gray-800 text-sm font-medium m-auto">
{{ index }}
</span>
</div>
</div>
</div>

<hr />

<!-- Summary -->
<div class="grid grid-cols-4 mx-2">
<div v-for="status in summaryStatuses" class="flex flex-col gap-1">
<div class="flex flex-row gap-1 items-center">
<span class="rounded full h-3 w-3" :class="`bg-${colorMap[status]}`" />
<span class="text-gray-600 text-sm font-medium leading-5"> {{ status }} </span>
</div>
<span class="text-gray-800 text-base font-semibold leading-6 mx-auto">
{{ summary[status] || 0 }}
</span>
</div>
</div>
</div>
</div>
</template>

<script setup>
import { computed, inject, ref, watch } from "vue"
import { createResource } from "frappe-ui"
const dayjs = inject("$dayjs")
const employee = inject("$employee")
const firstOfMonth = ref(dayjs().date(1).startOf("D"))
const colorMap = {
Present: "green-200",
"Work From Home": "green-200",
"Half Day": "yellow-100",
Absent: "red-100",
"On Leave": "blue-100",
Holiday: "gray-100",
}
const summaryStatuses = ["Present", "Half Day", "Absent", "On Leave"]
const summary = computed(() => {
const summary = {}
for (const status of Object.values(calendarEvents.data)) {
let updatedStatus = status === "Work From Home" ? "Present" : status
if (updatedStatus in summary) {
summary[updatedStatus] += 1
} else {
summary[updatedStatus] = 1
}
}
return summary
})
watch(
() => firstOfMonth.value,
() => {
calendarEvents.fetch()
}
)
const getEventOnDate = (date) => {
return calendarEvents.data[firstOfMonth.value.date(date).format("YYYY-MM-DD")]
}
const DAYS = ["S", "M", "T", "W", "T", "F", "S"]
//resources
const calendarEvents = createResource({
url: "hrms.api.get_attendance_calendar_events",
auto: true,
cache: "hrms:attendance_calendar_events",
makeParams() {
return {
employee: employee.data.name,
from_date: firstOfMonth.value.format("YYYY-MM-DD"),
to_date: firstOfMonth.value.endOf("M").format("YYYY-MM-DD"),
}
},
})
</script>
54 changes: 54 additions & 0 deletions frontend/src/components/AttendanceRequestItem.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<template>
<ListItem>
<template #left>
<AttendanceIcon class="h-5 w-5 text-gray-500" />
<div class="flex flex-col items-start gap-1.5">
<div class="text-base font-normal text-gray-800">
{{ props.doc.reason }}
</div>
<div class="text-xs font-normal text-gray-500">
<span>{{ props.doc.attendance_dates || getDates(props.doc) }}</span>
<span v-if="props.doc.to_date">
<span class="whitespace-pre"> &middot; </span>
<span class="whitespace-nowrap">{{
`${props.doc.total_attendance_days || getTotalDays(props.doc)}d`
}}</span>
</span>
</div>
</div>
</template>
<template #right>
<Badge variant="outline" :theme="colorMap[status]" :label="status" size="md" />
<FeatherIcon name="chevron-right" class="h-5 w-5 text-gray-500" />
</template>
</ListItem>
</template>

<script setup>
import { computed } from "vue"
import { Badge, FeatherIcon } from "frappe-ui"
import ListItem from "@/components/ListItem.vue"
import AttendanceIcon from "@/components/icons/AttendanceIcon.vue"
import { getDates, getTotalDays } from "@/data/attendance"
const props = defineProps({
doc: {
type: Object,
},
workflowStateField: {
type: String,
required: false,
},
})
const status = computed(() => {
if (props.workflowStateField) return props.doc[props.workflowStateField]
return props.doc.docstatus ? "Submitted" : "Draft"
})
const colorMap = {
Draft: "gray",
Submitted: "blue",
}
</script>
6 changes: 6 additions & 0 deletions frontend/src/components/BottomTabs.vue
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import HomeIcon from "@/components/icons/HomeIcon.vue"
import LeaveIcon from "@/components/icons/LeaveIcon.vue"
import ExpenseIcon from "@/components/icons/ExpenseIcon.vue"
import SalaryIcon from "@/components/icons/SalaryIcon.vue"
import AttendanceIcon from "@/components/icons/AttendanceIcon.vue"
const route = useRoute()
Expand All @@ -39,6 +40,11 @@ const tabItems = [
title: "Home",
route: "/home",
},
{
icon: AttendanceIcon,
title: "Attendance",
route: "/dashboard/attendance",
},
{
icon: LeaveIcon,
title: "Leaves",
Expand Down
20 changes: 7 additions & 13 deletions frontend/src/components/CheckInPanel.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@

<template v-if="settings.data?.allow_employee_checkin_from_mobile_app">
<div class="font-medium text-sm text-gray-500 mt-1.5" v-if="lastLog">
Last {{ lastLogType }} was at {{ lastLogTime }}
<span>Last {{ lastLogType }} was at {{ formatTimestamp(lastLog.time) }}</span>
<span class="whitespace-pre"> &middot; </span>
<router-link :to="{ name: 'EmployeeCheckinListView' }" v-slot="{ navigate }">
<span @click="navigate" class="underline">View List</span>
</router-link>
</div>
<Button
class="mt-4 mb-1 drop-shadow-sm py-5 text-base"
Expand Down Expand Up @@ -75,6 +79,8 @@ import { createResource, createListResource, toast, FeatherIcon } from "frappe-u
import { computed, inject, ref, onMounted, onBeforeUnmount } from "vue"
import { IonModal, modalController } from "@ionic/vue"

import { formatTimestamp } from "@/utils/formatters"

const DOCTYPE = "Employee Checkin"

const socket = inject("$socket")
Expand Down Expand Up @@ -115,18 +121,6 @@ const nextAction = computed(() => {
: { action: "IN", label: "Check In" }
})

const lastLogTime = computed(() => {
const timestamp = lastLog?.value?.time
const formattedTime = dayjs(timestamp).format("hh:mm a")

if (dayjs(timestamp).isToday()) return formattedTime
else if (dayjs(timestamp).isYesterday()) return `${formattedTime} yesterday`
else if (dayjs(timestamp).isSame(dayjs(), "year"))
return `${formattedTime} on ${dayjs(timestamp).format("D MMM")}`

return `${formattedTime} on ${dayjs(timestamp).format("D MMM, YYYY")}`
})

function handleLocationSuccess(position) {
latitude.value = position.coords.latitude
longitude.value = position.coords.longitude
Expand Down
83 changes: 33 additions & 50 deletions frontend/src/components/EmployeeAdvanceItem.vue
Original file line number Diff line number Diff line change
@@ -1,52 +1,37 @@
<template>
<div class="flex flex-col w-full justify-center gap-2.5">
<div class="flex flex-row items-center justify-between">
<div class="flex flex-row items-start gap-3 grow">
<EmployeeAdvanceIcon class="h-5 w-5 mt-[3px] text-gray-500" />
<div class="flex flex-col items-start gap-1">
<div
v-if="props.doc.balance_amount"
class="text-lg font-bold text-gray-800 leading-6"
>
{{ `${currency} ${props.doc.balance_amount} /` }}
<span class="text-gray-600">
{{ `${currency} ${props.doc.paid_amount}` }}
</span>
</div>
<div v-else class="text-lg font-bold text-gray-800 leading-6">
{{ `${currency} ${props.doc.advance_amount}` }}
</div>
<div class="text-xs font-normal text-gray-500">
<span>
{{ props.doc.purpose }}
</span>
<span class="whitespace-pre"> &middot; </span>
<span class="whitespace-nowrap">
{{ postingDate }}
</span>
</div>
<ListItem
:isTeamRequest="props.isTeamRequest"
:employee="props.doc.employee"
:employeeName="props.doc.employee_name"
>
<template #left>
<EmployeeAdvanceIcon class="h-5 w-5 mt-[3px] text-gray-500" />
<div class="flex flex-col items-start gap-1">
<div v-if="props.doc.balance_amount" class="text-lg font-bold text-gray-800 leading-6">
{{ `${currency} ${props.doc.balance_amount} /` }}
<span class="text-gray-600">
{{ `${currency} ${props.doc.paid_amount}` }}
</span>
</div>
<div v-else class="text-lg font-bold text-gray-800 leading-6">
{{ `${currency} ${props.doc.advance_amount}` }}
</div>
<div class="text-xs font-normal text-gray-500">
<span>
{{ props.doc.purpose }}
</span>
<span class="whitespace-pre"> &middot; </span>
<span class="whitespace-nowrap">
{{ postingDate }}
</span>
</div>
</div>
<div class="flex flex-row justify-end items-center gap-2">
<Badge
variant="outline"
:theme="colorMap[status]"
:label="status"
size="md"
/>
<FeatherIcon name="chevron-right" class="h-5 w-5 text-gray-500" />
</div>
</div>
<div
v-if="props.isTeamRequest"
class="flex flex-row items-center gap-2 pl-8"
>
<EmployeeAvatar :employeeID="props.doc.employee" />
<div class="text-sm text-gray-600 grow">
{{ props.doc.employee_name }}
</div>
</div>
</div>
</template>
<template #right>
<Badge variant="outline" :theme="colorMap[status]" :label="status" size="md" />
<FeatherIcon name="chevron-right" class="h-5 w-5 text-gray-500" />
</template>
</ListItem>
</template>

<script setup>
Expand All @@ -55,7 +40,7 @@ import { computed, inject } from "vue"
import { getCurrencySymbol } from "@/data/currencies"
import EmployeeAvatar from "@/components/EmployeeAvatar.vue"
import ListItem from "@/components/ListItem.vue"
import EmployeeAdvanceIcon from "@/components/icons/EmployeeAdvanceIcon.vue"
const dayjs = inject("$dayjs")
Expand Down Expand Up @@ -88,8 +73,6 @@ const postingDate = computed(() => {
})
const status = computed(() => {
return props.workflowStateField
? props.doc[props.workflowStateField]
: props.doc.status
return props.workflowStateField ? props.doc[props.workflowStateField] : props.doc.status
})
</script>
Loading

0 comments on commit 26dc3fe

Please sign in to comment.