Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(PWA): Leaves, Expense Claims, & Employee Advances #807

Merged
merged 92 commits into from
Aug 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
92 commits
Select commit Hold shift + click to select a range
3900db2
feat: set up form, list routing for leaves
ruchamahabal Jul 10, 2023
125a1f2
feat: Basic Formview and Fields Rendering
ruchamahabal Jul 12, 2023
b1aab2b
refactor(UX): show title with docname in link fields
ruchamahabal Jul 14, 2023
90d5e51
fix: bind attributes for fieldtypes
ruchamahabal Jul 14, 2023
48d21e3
feat: set up watchers for form fields to add scripts
ruchamahabal Jul 17, 2023
d969712
refactor: filter form fields to reduce noise
ruchamahabal Jul 17, 2023
9637b51
feat: show error messages for field validations
ruchamahabal Jul 17, 2023
61513a7
feat: Leave Application form scripts - set, validate values
ruchamahabal Jul 17, 2023
bf791e2
refactor: get all leave approval details in a single request
ruchamahabal Jul 17, 2023
1b8b7c9
fix: handle empty states in views
ruchamahabal Jul 22, 2023
7ce01d4
feat: populate leave approvers and allocated leave types
ruchamahabal Jul 24, 2023
5c0cb9a
feat: create document
ruchamahabal Jul 24, 2023
9048f2b
feat: disable save button until all the required fields are filled
ruchamahabal Jul 26, 2023
920daa1
chore: bulk format code using prettier
ruchamahabal Jul 26, 2023
1675982
chore: ignore formatting changes in blame
ruchamahabal Jul 26, 2023
4f4bee2
fix: socketio port in config
ruchamahabal Jul 26, 2023
d342101
feat: Edit form/Saved form view
ruchamahabal Jul 28, 2023
78d320f
feat: set leave application To Date based on From Date
ruchamahabal Jul 28, 2023
907a4c0
fix: make form header sticky
ruchamahabal Jul 28, 2023
8f6a380
feat: List view
ruchamahabal Jul 28, 2023
dab6d63
feat: status filter in list view
ruchamahabal Jul 29, 2023
4745421
feat: field level filters with conditions
ruchamahabal Jul 30, 2023
97c773d
feat: add expense claim routes and refactor leave routing
ruchamahabal Aug 2, 2023
625504c
feat: Expense Claim summary in dashboard
ruchamahabal Aug 2, 2023
f5c3170
feat: show recent expenses in Claims dashboard
ruchamahabal Aug 3, 2023
350901e
feat: show Employee Advance Balance
ruchamahabal Aug 3, 2023
4b732f3
feat: show claim status along with approval status
ruchamahabal Aug 3, 2023
7350f94
feat: Expense Claim list view
ruchamahabal Aug 3, 2023
e49dea9
refactor(UX): cleaned up Expense Claim form
ruchamahabal Aug 5, 2023
e808a39
feat: tabbed view in forms
ruchamahabal Aug 5, 2023
ca395e2
fix: make save button sticky
ruchamahabal Aug 6, 2023
bc8b34b
fix: allow removing status filter
ruchamahabal Aug 6, 2023
5619ad5
refactor: cleanup expense claim detail form
ruchamahabal Aug 6, 2023
7a37786
feat: expenses child table
ruchamahabal Aug 6, 2023
fb24614
refactor: cleanup expense taxes and charges table form
ruchamahabal Aug 7, 2023
0dea28b
fix: fix disabled prop type warnings
ruchamahabal Aug 7, 2023
6a71b42
fix: fields by tab logic
ruchamahabal Aug 7, 2023
e0eb8d6
feat: expense taxes & charges child table
ruchamahabal Aug 7, 2023
72f69ba
fix(Expense Claim): replace unnecessary `add_fetch` calls with doctyp…
ruchamahabal Aug 7, 2023
4e51a6b
feat: fetch expense claim type description on type selection
ruchamahabal Aug 7, 2023
307f494
chore: fix advance payments section break
ruchamahabal Aug 7, 2023
8e4cd6c
feat: settle claims against advances
ruchamahabal Aug 7, 2023
445bcc1
refactor(Expense Claim): extract child tables as separate components
ruchamahabal Aug 7, 2023
488f034
feat: Edit expense items & taxes
ruchamahabal Aug 8, 2023
406908d
feat: fetch expense approver for the current employee
ruchamahabal Aug 8, 2023
9685f91
feat: File attachments in Form view
ruchamahabal Aug 8, 2023
2c353c2
style: fix file uploader style
ruchamahabal Aug 8, 2023
794f4bf
style: make tabs sticky
ruchamahabal Aug 8, 2023
6e61322
style: use better icons
ruchamahabal Aug 8, 2023
c17f3f1
refactor: use autocomplete for filter operator selection
ruchamahabal Aug 8, 2023
dd2ae52
style: fix style on ios (status bar, disabled fields, form view botto…
ruchamahabal Aug 8, 2023
8dbb300
refactor: Leave Application form cleanup
ruchamahabal Jul 28, 2023
70fd001
refactor: clean-up `leave_application.js`
ruchamahabal Aug 1, 2023
ba7cdc3
fix: typecast tax rate during expense claim calculations
ruchamahabal Aug 12, 2023
ad0bc11
fix: show recent leaves in Leaves Dashboard for consistency with expe…
ruchamahabal Aug 12, 2023
a26d334
fix: shallow copy child table items while editing
ruchamahabal Aug 12, 2023
25bf58f
fix: recalculate taxes after on adding/editing expenses
ruchamahabal Aug 12, 2023
55e4392
fix: avoid accidentally selecting/de-selecting advance cards on editi…
ruchamahabal Aug 12, 2023
9e7d5a4
fix: advance & grand total calculations
ruchamahabal Aug 12, 2023
d3be713
feat: apply link field filters
ruchamahabal Aug 13, 2023
25479a8
fix(Expense Claim): set default cost center
ruchamahabal Aug 13, 2023
a286020
fix: relative content going above sticky tabs & header on scroll
ruchamahabal Aug 14, 2023
ac73815
fix: empty state for advances
ruchamahabal Aug 14, 2023
e2e4d77
feat: set form as read only if session user is not the approver/owner
ruchamahabal Aug 14, 2023
968a834
fix: advance selection while editing expenses
ruchamahabal Aug 14, 2023
232b0e3
fix: clean-up tabs
ruchamahabal Aug 14, 2023
ab9a0e1
feat(Edit doc): delete, upload & fetch existing attachments
ruchamahabal Aug 14, 2023
6ee4242
feat: show status indicator on saved doc
ruchamahabal Aug 15, 2023
baff176
refactor: move expense claim list config to doctype settings
ruchamahabal Aug 15, 2023
a04cff3
feat: show colored status badge on saved form
ruchamahabal Aug 15, 2023
d409413
feat: guess status color if doctype states are not configured
ruchamahabal Aug 18, 2023
692b039
fix: order lists by modified desc
ruchamahabal Aug 18, 2023
da419f4
fix(form routing): re-route to saved form view on new record insertion
ruchamahabal Aug 18, 2023
9b5614e
feat: submit & cancel doc
ruchamahabal Aug 19, 2023
900a370
fix: set submitted/cancelled form as read-only
ruchamahabal Aug 19, 2023
7d496e3
fix(forms): respect perms for submit & cancel
ruchamahabal Aug 19, 2023
7ee2b54
refactor: cleanup employee advance form
ruchamahabal Aug 19, 2023
45b879f
feat: Employee Advance form
ruchamahabal Aug 19, 2023
a50e133
feat: employee advance list view
ruchamahabal Aug 19, 2023
432b39c
fix: add attachment links
ruchamahabal Aug 20, 2023
68bab37
feat: unified Request Panel - Leaves + Claims
ruchamahabal Aug 20, 2023
b253e56
refactor: action sheets for requests
ruchamahabal Aug 20, 2023
a4fad8d
feat: Expense Claim Request Action Sheet
ruchamahabal Aug 20, 2023
0057c04
feat: add form link in action sheet
ruchamahabal Aug 20, 2023
2d2dab6
refactor: Approval/Rejection -> Submission/Cancellation flow
ruchamahabal Aug 20, 2023
6b8be22
fix: links for employee advance docs & list view
ruchamahabal Aug 20, 2023
2db5028
fix: add quick links
ruchamahabal Aug 20, 2023
850b657
feat(form): add delete & reload menu options
ruchamahabal Aug 21, 2023
56dfe73
fix: confirm before delete, submit, cancel operations
ruchamahabal Aug 21, 2023
0d35b77
feat: delete child table row
ruchamahabal Aug 21, 2023
80331de
feat: realtime doc list & request updates
ruchamahabal Aug 21, 2023
83d39a6
fix: modal height exceeding viewport
ruchamahabal Aug 22, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .git-blame-ignore-revs
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,7 @@
b55d6e27af6bd274dfa47e66a3012ddec68ce798

# bulk formatting PWA frontend code
f37f15b2b5329e3b0b35891e1c4fd82f48562c6d
f37f15b2b5329e3b0b35891e1c4fd82f48562c6d

# bulk formatting PWA frontend code
920daa1a3ddccaefaf7b9348f850831d6e0a0e6b
2 changes: 2 additions & 0 deletions frontend/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-title" content="Frappe HR" />
<meta name="apple-mobile-web-app-status-bar-style" content="white" />
<!-- required for setting the status bar bg as white -->
<meta name="theme-color" content="#fff" />

<!-- PWA -->
<link
Expand Down
4 changes: 2 additions & 2 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
"copy-html-entry": "cp ../hrms/public/frontend/index.html ../hrms/www/hrms.html"
},
"dependencies": {
"@ionic/vue": "^7.0.4",
"@ionic/vue-router": "^7.0.4",
"@ionic/vue": "^7.2.1",
"@ionic/vue-router": "^7.2.1",
"dayjs": "^1.11.7",
"feather-icons": "^4.28.0",
"frappe-ui": "^0.0.112",
Expand Down
37 changes: 37 additions & 0 deletions frontend/src/components/EmployeeAdvanceBalance.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<template>
<div
class="flex flex-col bg-white rounded-lg mt-5 overflow-auto"
v-if="props.items?.length"
>
<router-link
v-for="link in props.items"
:key="link.name"
:to="{ name: 'EmployeeAdvanceDetailView', params: { id: link.name } }"
class="flex flex-row p-3.5 items-center justify-between border-b cursor-pointer"
>
<EmployeeAdvanceItem :doc="link" />
</router-link>

<router-link
:to="{ name: 'EmployeeAdvanceFormView' }"
v-slot="{ navigate }"
>
<div class="flex flex-col bg-white w-full py-5 px-3.5 mt-0 border-none">
<Button @click="navigate" appearance="secondary" class="py-2">
Request an Advance
</Button>
</div>
</router-link>
</div>
<EmptyState message="You have no advances" v-else />
</template>

<script setup>
import EmployeeAdvanceItem from "@/components/EmployeeAdvanceItem.vue"

const props = defineProps({
items: {
type: Array,
},
})
</script>
80 changes: 80 additions & 0 deletions frontend/src/components/EmployeeAdvanceItem.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
<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">
<WalletIcon class="h-4 w-4 mt-0.5 text-gray-500" />
<div class="flex flex-col items-start">
<div
v-if="props.doc.balance_amount"
class="text-xl 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-xl font-bold text-gray-800 leading-6">
{{ `${currency} ${props.doc.advance_amount}` }}
</div>
<div class="text-sm 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>
<div class="flex flex-row justify-end items-center gap-2">
<Badge :colorMap="colorMap" :label="props.doc.status" />
<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>

<script setup>
import { FeatherIcon, Badge } from "frappe-ui"
import { computed, inject } from "vue"

import { getCurrencySymbol } from "@/data/currencies"

import EmployeeAvatar from "@/components/EmployeeAvatar.vue"
import WalletIcon from "@/components/icons/WalletIcon.vue"

const dayjs = inject("$dayjs")
const props = defineProps({
doc: {
type: Object,
},
isTeamRequest: {
type: Boolean,
default: false,
},
})

const colorMap = {
Paid: "green",
Unpaid: "yellow",
Claimed: "blue",
Returned: "gray",
"Partly Claimed and Returned": "yellow",
}

const currency = computed(() => getCurrencySymbol(props.doc.currency))

const postingDate = computed(() => {
return dayjs(props.doc.posting_date).format("D MMM")
})
</script>
13 changes: 13 additions & 0 deletions frontend/src/components/EmptyState.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<template>
<div
class="text-sm text-gray-500 rounded-lg flex flex-col items-center bg-gray-100 p-5"
>
{{ props.message }}
</div>
</template>

<script setup>
const props = defineProps({
message: { type: String },
})
</script>
83 changes: 83 additions & 0 deletions frontend/src/components/ExpenseAdvancesTable.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
<template>
<div class="flex flex-row justify-between items-center">
<h2 class="text-lg font-semibold text-gray-800">Settle against Advances</h2>
</div>

<div class="flex flex-col gap-2.5" v-if="expenseClaim.advances?.length">
<!-- Advance Card -->
<div
v-for="advance in expenseClaim.advances"
:key="advance.name"
class="flex flex-col bg-white border shadow-sm rounded-lg p-3.5"
:class="[
advance.selected ? 'border-blue-500' : '',
isReadOnly ? '' : 'cursor-pointer',
]"
@click="toggleAdvanceSelection(advance)"
>
<div class="flex flex-row justify-between items-center">
<div class="flex flex-row items-start gap-3">
<Input
type="checkbox"
class="mt-0.5"
v-model="advance.selected"
:disabled="isReadOnly"
/>

<div class="flex flex-col items-start gap-1">
<div class="text-lg font-semibold text-gray-800">
{{ advance.purpose || advance.employee_advance }}
</div>
<div class="flex flex-row items-center gap-3 justify-between">
<div class="text-sm font-normal text-gray-500">
{{
`Unclaimed Amount: ${currency} ${advance.unclaimed_amount}`
}}
</div>
</div>
</div>
</div>

<div class="flex flex-row items-center gap-2">
<span class="text-normal">
{{ currency }}
</span>
<Input
type="number"
class="w-20"
v-model="advance.allocated_amount"
@input="(v) => (advance.selected = v)"
@click.stop
:disabled="isReadOnly"
:max="advance.unclaimed_amount"
min="0"
/>
</div>
</div>
</div>
</div>

<EmptyState message="No advances found" v-else />
</template>

<script setup>
const props = defineProps({
expenseClaim: {
type: Object,
required: true,
},
currency: {
type: String,
required: true,
},
isReadOnly: {
type: Boolean,
default: false,
},
})

function toggleAdvanceSelection(advance) {
if (props.isReadOnly) return
advance.selected = !advance.selected
}
</script>
98 changes: 98 additions & 0 deletions frontend/src/components/ExpenseClaimItem.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
<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">
<ExpenseIcon class="h-5 w-5 mt-0.5 text-gray-500" />
<div class="flex flex-col items-start">
<div class="text-lg font-normal text-gray-800">
{{ claimTitle }}
</div>
<div class="text-sm font-normal text-gray-500">
<span>
{{ `${currency} ${props.doc.total_claimed_amount}` }}
</span>
<span class="whitespace-pre"> &middot; </span>
<span class="whitespace-nowrap">
{{ claimDates }}
</span>
</div>
</div>
</div>
<div class="flex flex-row justify-end items-center gap-2">
<span
class="text-gray-600 bg-gray-100 font-medium rounded-lg text-xs px-2"
>
{{ props.doc.status }}
</span>
<Badge :colorMap="colorMap" :label="approvalStatus" />
<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>

<script setup>
import { FeatherIcon, Badge } from "frappe-ui"
import { computed, inject } from "vue"

import { getCompanyCurrencySymbol } from "@/data/currencies"

import EmployeeAvatar from "@/components/EmployeeAvatar.vue"
import ExpenseIcon from "@/components/icons/ExpenseIcon.vue"

const dayjs = inject("$dayjs")
const props = defineProps({
doc: {
type: Object,
},
isTeamRequest: {
type: Boolean,
default: false,
},
})

const colorMap = {
Approved: "green",
Rejected: "red",
Pending: "yellow",
}

const claimTitle = computed(() => {
let title = props.doc.expense_type
if (props.doc.total_expenses > 1) {
title += ` & ${props.doc.total_expenses - 1} more`
}

return title
})

const claimDates = computed(() => {
if (!props.doc.from_date && !props.doc.to_date)
return dayjs(props.doc.posting_date).format("D MMM")

if (props.doc.from_date === props.doc.to_date) {
return dayjs(props.doc.from_date).format("D MMM")
} else {
return `${dayjs(props.doc.from_date).format("D MMM")} - ${dayjs(
props.doc.to_date
).format("D MMM")}`
}
})

const currency = computed(() => getCompanyCurrencySymbol(props.doc.company))

const approvalStatus = computed(() => {
return props.doc.approval_status === "Draft"
? "Pending"
: props.doc.approval_status
})
</script>
Loading
Loading