Skip to content

Commit

Permalink
imp: bonus & deduction for payslip
Browse files Browse the repository at this point in the history
  • Loading branch information
BigSillyTiger committed Apr 5, 2024
1 parent a999365 commit 467ca9e
Show file tree
Hide file tree
Showing 18 changed files with 584 additions and 183 deletions.
9 changes: 9 additions & 0 deletions src/configs/i18n/scriptEn.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ const en = {
},
btn: {
addDate: "Add Date",
addNewBonus: "Add New Bonus",
addNewDeduction: "Add New Deduction",
append: "Append",
addClient: "Register New Client",
addStuff: "Register New Staff",
Expand Down Expand Up @@ -119,12 +121,14 @@ const en = {
addr: "Addr",
address: "Address",
addrJob: "Job Address",
amount: "Amount",
assignStaff: "Assign Staff",
assignedStaff: "Assigned Staff",
aud: "AUD",
balance: "Balance",
billTo: "Bill To",
bld: "BLD",
bonus: "Bonus",
bsb: "BSB",
break: "Break",
bank: "Bank Account",
Expand All @@ -138,9 +142,11 @@ const en = {
completed: "Completed",
companyInfo: "Company Info",
confirmedWU: "Confirmed Work Units",
confirmedWL: "Confirmed Work Logs",
country: "Country",
date: "Date",
datePicker: "Date Picker",
deduction: "Deduction",
defaultUnit: "Default Unit",
defaultPrice: "Default Price",
deposit: "Deposit",
Expand Down Expand Up @@ -171,6 +177,7 @@ const en = {
manager: "Manager",
menu: "Menu",
name: "Name",
note: "Note",
netto: "Netto",
newIssueDate: "New Issue Date",
none: "None",
Expand All @@ -194,6 +201,8 @@ const en = {
paymentTo: "Payment To",
payDate: "Paid Date",
payslip: "Payslip",
payPeriod: "Pay Period",
payTo: "Pay To",
pending: "Pending",
period: "Work Period",
pc: "Poscode",
Expand Down
8 changes: 4 additions & 4 deletions src/configs/schema/payslipSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,16 @@ const payslipSchema = z.object({
});

const bonusSchema = z.object({
bsid: z.string().default(""),
fk_psid: z.string().default(""),
fk_uid: z.string().default(""),
bs_note: z.string().trim().nullable().default(""),
note: z.string().trim().nullable().default(""),
amount: z.number().default(0),
});

const deductionSchema = z.object({
did: z.string().default(""),
fk_psid: z.string().default(""),
fk_uid: z.string().default(""),
bs_note: z.string().trim().nullable().default(""),
note: z.string().trim().nullable().default(""),
amount: z.number().default(0),
});

Expand All @@ -34,3 +32,5 @@ const payslipsSchema = payslipSchema.extend({
});

export type Tpayslips = z.infer<typeof payslipsSchema>;
export type Tbonus = z.infer<typeof bonusSchema>;
export type Tdedcution = z.infer<typeof deductionSchema>;
58 changes: 58 additions & 0 deletions src/configs/zustore/payslipStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,84 @@ import { useStore } from "zustand";
import { createStore } from "zustand/vanilla";
import { DateRange } from "react-day-picker";
import { TwlTableRow } from "../schema/workSchema";
import { Tbonus, Tdedcution } from "../schema/payslipSchema";

type Tstate = {
dayRange: DateRange;
staffWL: TwlTableRow[];
bonus: Partial<Tbonus>[];
dedcution: Partial<Tdedcution>[];
};

type Taction = {
setDayRange: (range: DateRange | undefined) => void;
setStaffWL: (worklogs: TwlTableRow[]) => void;
/* bonus */
setBonusAmount: (index: number, amount: number) => void;
setBonusNote: (index: number, note: string) => void;
appendBonus: (bonus: Partial<Tbonus>) => void;
removeBonus: (index: number) => void;
/* deduction */
setDeductionAmount: (index: number, amount: number) => void;
setDeductionNote: (index: number, note: string) => void;
appendDeduction: (dedcution: Partial<Tdedcution>) => void;
removeDeduction: (index: number) => void;
};

export const payslipStore = createStore<Tstate & Taction>((set) => ({
dayRange: { from: undefined, to: undefined },
staffWL: [],
bonus: [],
dedcution: [],
setDayRange: (range: DateRange | undefined) => set({ dayRange: range }),
setStaffWL: (worklogs: TwlTableRow[]) =>
set((state) => ({
...state,
staffWL: worklogs,
})),
/* bonus */
setBonusAmount: (index: number, amount: number) =>
set((state) => {
const newBonus = [...state.bonus];
newBonus[index] = { ...newBonus[index], amount };
return { ...state, bonus: newBonus };
}),
setBonusNote: (index: number, note: string) =>
set((state) => {
const newBonus = [...state.bonus];
newBonus[index] = { ...newBonus[index], note };
return { ...state, bonus: newBonus };
}),
appendBonus: (bonus: Partial<Tbonus>) =>
set((state) => ({ ...state, bonus: [...state.bonus, bonus] })),
removeBonus: (index: number) =>
set((state) => {
const newBonus = state.bonus.filter((_, i) => i !== index);
return { ...state, bonus: newBonus };
}),
/* deduction */
setDeductionAmount: (index: number, amount: number) =>
set((state) => {
const newDeduction = [...state.dedcution];
newDeduction[index] = { ...newDeduction[index], amount };
return { ...state, dedcution: newDeduction };
}),
setDeductionNote: (index: number, note: string) =>
set((state) => {
const newDeduction = [...state.dedcution];
newDeduction[index] = { ...newDeduction[index], note };
return { ...state, dedcution: newDeduction };
}),
appendDeduction: (dedcution: Partial<Tdedcution>) =>
set((state) => ({
...state,
dedcution: [...state.dedcution, dedcution],
})),
removeDeduction: (index: number) =>
set((state) => {
const newDeduction = state.dedcution.filter((_, i) => i !== index);
return { ...state, dedcution: newDeduction };
}),
}));

export const usePayslipStore = <T>(selector: (state: Tstate & Taction) => T) =>
Expand Down
2 changes: 1 addition & 1 deletion src/lib/time.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export const auToISO = (date: string) => {
* @returns yyy-MM-dd is used for datepicker
*/
export const dateFormat = (
date: Date | string | null,
date: Date | string | null | undefined,
form: "au" | "iso" = "iso"
) => {
if (date instanceof Date) {
Expand Down
125 changes: 125 additions & 0 deletions src/pageComponents/modals/mPayslip/Bonus.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
import type { FC } from "react";
import { useState } from "react";
import Card from "@/components/card";
import { Button } from "@/components/ui/button";
import { useTranslation } from "react-i18next";
import { usePayslipStore } from "@/configs/zustore";
import { XBtn } from "@/components/btns";

const Bonus: FC = () => {
const [t] = useTranslation();
const bonus = usePayslipStore((state) => state.bonus);
const removeBonus = usePayslipStore((state) => state.removeBonus);
const appendBonus = usePayslipStore((state) => state.appendBonus);
const setBonusAmount = usePayslipStore((state) => state.setBonusAmount);
const setBonusNote = usePayslipStore((state) => state.setBonusNote);
const [amount, setAmount] = useState<number>(0);
const [note, setNote] = useState("");
let timeoutID: NodeJS.Timeout | null = null;

const handleAddBonus = () => {
appendBonus({ amount: 0, note: "" });
};

const AddBtn = () => {
return (
<section className="w-full flex justify-center mt-2">
<Button
className="bg-indigo-600 text-slate-50 text-xl hover:bg-slate-50 hover:text-indigo-600 border-2 border-indigo-700"
onClick={handleAddBonus}
>
{t("btn.addNewBonus")}
</Button>
</section>
);
};

const bonusList = bonus.map((b, i) => {
return (
<section
key={i}
className="col-span-full grid grid-cols-10 gap-x-2 gap-y-2 "
>
{/* x btn */}
<section className="col-span-1 m-auto">
<XBtn
onClick={() => {
removeBonus(i);
}}
/>
</section>
<Card className="col-span-9 grid grid-cols-6 gap-x-4">
<section className="col-span-2">
<label
htmlFor="amount"
className="block text-sm font-medium leading-6 text-gray-900"
>
{t("label.amount")}
</label>
<input
id="amount"
type="number"
step="0.01"
min={0}
defaultValue={b.amount}
onChange={(e) => {
setAmount(parseInt(e.target.value));
timeoutID && clearTimeout(timeoutID);
timeoutID = setTimeout(() => {
setBonusAmount(i, parseInt(e.target.value));
}, 1500);
}}
onBlur={() => {
if (timeoutID) {
clearTimeout(timeoutID);
setBonusAmount(i, amount);
}
}}
className="outline-none h-9 block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6 pl-2"
/>
</section>
<section className="col-span-4">
<label
htmlFor="note"
className="block text-sm font-medium leading-6 text-gray-900"
>
{t("label.note")}
</label>
<textarea
id="note"
rows={1}
defaultValue={b?.note ? b.note : ""}
onChange={(e) => {
setNote(e.target.value);
timeoutID && clearTimeout(timeoutID);
timeoutID = setTimeout(() => {
setBonusNote(i, e.target.value);
}, 1500);
}}
onBlur={() => {
if (timeoutID) {
clearTimeout(timeoutID);
setBonusNote(i, note);
}
}}
className="outline-none h-9 block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6 pl-2"
/>
</section>
</Card>
</section>
);
});

return (
<>
{bonusList.length ? (
<Card className="max-h-[40dvh] overflow-y-auto flex flex-col gap-y-3">
{bonusList}
</Card>
) : null}
<AddBtn />
</>
);
};

export default Bonus;
Loading

0 comments on commit 467ca9e

Please sign in to comment.