Skip to content

Commit 07a31f5

Browse files
committedSep 30, 2023
mysaly app coming
1 parent 7f5e637 commit 07a31f5

24 files changed

+2719
-1
lines changed
 

‎.eslintrc.cjs

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
module.exports = {
2+
root: true,
3+
env: { browser: true, es2020: true },
4+
extends: [
5+
'eslint:recommended',
6+
'plugin:@typescript-eslint/recommended',
7+
'plugin:react-hooks/recommended',
8+
],
9+
ignorePatterns: ['dist', '.eslintrc.cjs'],
10+
parser: '@typescript-eslint/parser',
11+
plugins: ['react-refresh'],
12+
rules: {
13+
'react-refresh/only-export-components': [
14+
'warn',
15+
{ allowConstantExport: true },
16+
],
17+
},
18+
}

‎.gitignore

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# Logs
2+
logs
3+
*.log
4+
npm-debug.log*
5+
yarn-debug.log*
6+
yarn-error.log*
7+
pnpm-debug.log*
8+
lerna-debug.log*
9+
10+
node_modules
11+
dist
12+
dist-ssr
13+
*.local
14+
15+
# Editor directories and files
16+
.vscode/*
17+
!.vscode/extensions.json
18+
.idea
19+
.DS_Store
20+
*.suo
21+
*.ntvs*
22+
*.njsproj
23+
*.sln
24+
*.sw?

‎README.md

+17-1
Original file line numberDiff line numberDiff line change
@@ -1 +1,17 @@
1-
# mysaly
1+
<p align="center">
2+
<a href="https://vixaticaicons.vercel.app/#" target="_blank">
3+
<img src="https://raw.githubusercontent.com/njirolu/vixatica-icons/main/static/img/logo.svg" alt="Vixaticaicons" width="300">
4+
</a>
5+
</p>
6+
7+
<p align="center">
8+
Cek gaji bulanan kamu setelah potong pajak dan iuran BPJS dengan kalkulator gaji. Gampang banget, cuma butuh sebentar!
9+
<p>
10+
11+
<p align="center">
12+
<a href="https://vixaticaicons.vercel.app/"><strong>Hitung disini &rarr;</strong></a>
13+
</p>
14+
15+
## License
16+
17+
This library is MIT licensed.

‎index.html

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<!doctype html>
2+
<html lang="en">
3+
4+
<head>
5+
<meta charset="UTF-8" />
6+
<link rel="icon" type="image/svg+xml" href="/favicon.ico" />
7+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
8+
<link rel="preconnect" href="https://fonts.googleapis.com">
9+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
10+
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
11+
<meta name="description"
12+
content="Cek gaji bulanan kamu setelah potong pajak dan iuran BPJS dengan kalkulator gaji. Gampang banget, cuma butuh sebentar!">
13+
<title>MySaly: Kalkulator Gaji</title>
14+
</head>
15+
16+
17+
18+
<body class="min-h-screen font-[Inter]">
19+
<div id="root"></div>
20+
<script type="module" src="/src/main.tsx"></script>
21+
</body>
22+
23+
</html>

‎logo.png

32.3 KB
Loading

‎package.json

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
{
2+
"name": "mysaly",
3+
"private": true,
4+
"version": "0.0.0",
5+
"type": "module",
6+
"scripts": {
7+
"dev": "vite",
8+
"build": "tsc && vite build",
9+
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
10+
"preview": "vite preview"
11+
},
12+
"dependencies": {
13+
"@headlessui/react": "^1.7.17",
14+
"@heroicons/react": "^2.0.18",
15+
"react": "^18.2.0",
16+
"react-dom": "^18.2.0"
17+
},
18+
"devDependencies": {
19+
"@types/react": "^18.2.15",
20+
"@types/react-dom": "^18.2.7",
21+
"@typescript-eslint/eslint-plugin": "^6.0.0",
22+
"@typescript-eslint/parser": "^6.0.0",
23+
"@vitejs/plugin-react-swc": "^3.3.2",
24+
"autoprefixer": "^10.4.16",
25+
"eslint": "^8.45.0",
26+
"eslint-plugin-react-hooks": "^4.6.0",
27+
"eslint-plugin-react-refresh": "^0.4.3",
28+
"postcss": "^8.4.30",
29+
"tailwindcss": "^3.3.3",
30+
"typescript": "^5.0.2",
31+
"vite": "^4.4.5"
32+
}
33+
}

‎postcss.config.js

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
export default {
2+
plugins: {
3+
tailwindcss: {},
4+
autoprefixer: {},
5+
},
6+
}

‎public/favicon.ico

4.06 KB
Binary file not shown.

‎src/App.tsx

+245
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,245 @@
1+
import React from "react";
2+
import InputCurrency from "./components/ui/input-currency";
3+
import { Listbox, Transition } from "@headlessui/react";
4+
import { CheckIcon, ChevronUpDownIcon } from "@heroicons/react/20/solid";
5+
import Footer from "./components/footer";
6+
import Calc from "./components/calc";
7+
import CoWorkersIllustration from "./components/ui/co-workers-illustration";
8+
import Info from "./components/info";
9+
10+
const MARITAL_STATUS = [
11+
{ label: "Belum Kawin", value: "TK" },
12+
{ label: "Kawin", value: "K" },
13+
];
14+
15+
const DEPENDENTS = [
16+
{ label: "0", value: "0" },
17+
{ label: "1", value: "1" },
18+
{ label: "2", value: "2" },
19+
{ label: "3", value: "3" },
20+
];
21+
22+
interface SelectState {
23+
label: string;
24+
value: string;
25+
}
26+
27+
interface FormState {
28+
grossSalary: number;
29+
maritalStatus: SelectState;
30+
dependents: SelectState;
31+
}
32+
33+
function App() {
34+
const [grossSalary, setGrossSalary] = React.useState<number>(0);
35+
const [maritalStatusSelected, setMaritalStatusSelected] =
36+
React.useState<SelectState>(MARITAL_STATUS[0]);
37+
const [dependentsSelected, setDependentsSelected] =
38+
React.useState<SelectState>(DEPENDENTS[0]);
39+
40+
const [form, setForm] = React.useState<FormState>({
41+
grossSalary: 0,
42+
maritalStatus: { label: "Belum Kawin", value: "TK" },
43+
dependents: { label: "0", value: "0" },
44+
});
45+
46+
function submit() {
47+
setGrossSalary(form.grossSalary);
48+
setMaritalStatusSelected(form.maritalStatus);
49+
setDependentsSelected(form.dependents);
50+
}
51+
52+
return (
53+
<div className="min-h-screen flex flex-col pt-10">
54+
<main>
55+
<section className="flex flex-col lg:max-w-[1200px] max-w-[100%] px-6 md:mx-auto items-center justify-center gap-4 mb-20">
56+
<h1 className="text-3xl font-bold leading-tight tracking-tighter md:text-5xl lg:leading-[1.1]">
57+
MySaly
58+
</h1>
59+
<p className="text-sm md:text-xl text-center md:text-left font-normal">
60+
Kalkulator Gaji ini bikin gampang banget buat ngitung gaji bersih
61+
bulanan kamu, jadi nggak perlu repot lagi!
62+
</p>
63+
</section>
64+
65+
<section className="flex flex-col md:flex-row lg:max-w-[1200px] md:mx-auto max-w-[100%] px-6 mb-10 md:gap-10">
66+
<div className="w-full md:w-[45%] flex flex-col gap-4 mb-6 md:mb-0">
67+
<div className="flex flex-col gap-2">
68+
<label className="font-bold text-sm">Gaji Kotor Bulanan</label>
69+
<InputCurrency
70+
onChange={(value) =>
71+
setForm((prevState) => ({ ...prevState, grossSalary: value }))
72+
}
73+
value={form.grossSalary}
74+
/>
75+
</div>
76+
77+
<div className="flex gap-2">
78+
<div className="flex flex-1 flex-col gap-2">
79+
<label className="font-bold text-sm">Status perkawinan</label>
80+
<Listbox
81+
value={form.maritalStatus.value}
82+
onChange={(e) =>
83+
setForm((prevState) => ({
84+
...prevState,
85+
maritalStatus: e as unknown as SelectState,
86+
}))
87+
}
88+
>
89+
<div className="relative mt-1">
90+
<Listbox.Button className="relative w-full cursor-default py-2 pl-3 pr-10 text-left rounded-md bg-white ring-1 ring-gray-300 px-4 font-thin outline-none transition-all duration-200 ease-in-out focus:bg-white focus:ring-2 focus:ring-blue-400">
91+
<span className="block truncate">
92+
{form.maritalStatus.label}
93+
</span>
94+
<span className="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2">
95+
<ChevronUpDownIcon
96+
className="h-5 w-5 text-gray-400"
97+
aria-hidden="true"
98+
/>
99+
</span>
100+
</Listbox.Button>
101+
<Transition
102+
as={React.Fragment}
103+
leave="transition ease-in duration-100"
104+
leaveFrom="opacity-100"
105+
leaveTo="opacity-0"
106+
>
107+
<Listbox.Options className="absolute mt-1 max-h-60 w-full overflow-auto rounded-md bg-white py-1 text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none sm:text-sm">
108+
{MARITAL_STATUS.map(
109+
(maritalStatus, maritalStatusIdx) => (
110+
<Listbox.Option
111+
key={maritalStatusIdx}
112+
className={({ active }) =>
113+
`relative cursor-default select-none py-2 pl-10 pr-4 ${
114+
active
115+
? "bg-gray-200 text-black"
116+
: "text-gray-900"
117+
}`
118+
}
119+
value={maritalStatus}
120+
>
121+
{({ selected }) => (
122+
<>
123+
<span
124+
className={`block truncate ${
125+
selected ? "font-medium" : "font-normal"
126+
}`}
127+
>
128+
{maritalStatus.label}
129+
</span>
130+
{selected ? (
131+
<span className="absolute inset-y-0 left-0 flex items-center pl-3 text-amber-600">
132+
<CheckIcon
133+
className="h-5 w-5"
134+
aria-hidden="true"
135+
/>
136+
</span>
137+
) : null}
138+
</>
139+
)}
140+
</Listbox.Option>
141+
)
142+
)}
143+
</Listbox.Options>
144+
</Transition>
145+
</div>
146+
</Listbox>
147+
</div>
148+
149+
<div className="flex flex-1 flex-col gap-2">
150+
<label className="font-bold text-sm">Jumlah Tanggungan</label>
151+
<Listbox
152+
value={form.dependents.value}
153+
onChange={(e) =>
154+
setForm((prevState) => ({
155+
...prevState,
156+
dependents: e as unknown as SelectState,
157+
}))
158+
}
159+
>
160+
<div className="relative mt-1">
161+
<Listbox.Button className="relative w-full cursor-default py-2 pl-3 pr-10 text-left rounded-md bg-white ring-1 ring-gray-300 px-4 font-thin outline-none transition-all duration-200 ease-in-out focus:bg-white focus:ring-2 focus:ring-blue-400">
162+
<span className="block truncate">
163+
{form.dependents.label}
164+
</span>
165+
<span className="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2">
166+
<ChevronUpDownIcon
167+
className="h-5 w-5 text-gray-400"
168+
aria-hidden="true"
169+
/>
170+
</span>
171+
</Listbox.Button>
172+
<Transition
173+
as={React.Fragment}
174+
leave="transition ease-in duration-100"
175+
leaveFrom="opacity-100"
176+
leaveTo="opacity-0"
177+
>
178+
<Listbox.Options className="absolute mt-1 max-h-60 w-full overflow-auto rounded-md bg-white py-1 text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none sm:text-sm">
179+
{DEPENDENTS.map((dependent, dependentIdx) => (
180+
<Listbox.Option
181+
key={dependentIdx}
182+
className={({ active }) =>
183+
`relative cursor-default select-none py-2 pl-10 pr-4 ${
184+
active
185+
? "bg-gray-200 text-black"
186+
: "text-gray-900"
187+
}`
188+
}
189+
value={dependent}
190+
>
191+
{({ selected }) => (
192+
<>
193+
<span
194+
className={`block truncate ${
195+
selected ? "font-medium" : "font-normal"
196+
}`}
197+
>
198+
{dependent.label}
199+
</span>
200+
{selected ? (
201+
<span className="absolute inset-y-0 left-0 flex items-center pl-3 text-amber-600">
202+
<CheckIcon
203+
className="h-5 w-5"
204+
aria-hidden="true"
205+
/>
206+
</span>
207+
) : null}
208+
</>
209+
)}
210+
</Listbox.Option>
211+
))}
212+
</Listbox.Options>
213+
</Transition>
214+
</div>
215+
</Listbox>
216+
</div>
217+
</div>
218+
<button
219+
onClick={submit}
220+
className="inline-flex md:self-end items-center justify-center rounded-full text-md font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-400 disabled:pointer-events-none disabled:opacity-50 bg-gray-900 text-white hover:bg-gray-700 h-9 px-7 py-5 shadow-md"
221+
>
222+
Hitung Gaji
223+
</button>
224+
</div>
225+
<div className="flex-1 grow flex md:justify-end justify-center">
226+
{grossSalary > 0 ? (
227+
<Calc
228+
grossSalary={grossSalary}
229+
maritalStatus={maritalStatusSelected.value}
230+
dependents={dependentsSelected.value}
231+
/>
232+
) : (
233+
<CoWorkersIllustration />
234+
)}
235+
</div>
236+
</section>
237+
238+
<Info />
239+
</main>
240+
<Footer />
241+
</div>
242+
);
243+
}
244+
245+
export default App;

‎src/components/calc.tsx

+213
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,213 @@
1+
import { format } from "../utils/currency";
2+
3+
interface CalcProps {
4+
grossSalary: number;
5+
maritalStatus: string;
6+
dependents: string;
7+
}
8+
9+
interface IPTKPRate {
10+
[key: string]: number;
11+
}
12+
13+
const NUMBER_OF_MONTHS_IN_A_YEAR = 12;
14+
const POSITION_COST_PERCENTAGE = 0.05;
15+
16+
const PERCENTAGE_OF_BPJS_HEALTH_COVERED_BY_EMPLOYEES = 0.01;
17+
const PERCENTAGE_OF_BPJS_HEALTH_COVERED_BY_COMPANY = 0.04;
18+
19+
const PERCENTAGE_OF_BPJS_EMPLOYEMENT_COVERED_BY_EMPLOYEES = 0.02;
20+
const PERCENTAGE_OF_BPJS_EMPLOYEMENT_COVERED_BY_COMPANY = 0.037;
21+
22+
const PTKP_RATE = {
23+
"TK/0": 54_000_000,
24+
"TK/1": 58_500_000,
25+
"TK/2": 63_000_000,
26+
"TK/3": 67_500_000,
27+
"K/0": 58_500_000,
28+
"K/1": 63_000_000,
29+
"K/2": 67_500_000,
30+
"K/3": 7_200_0000,
31+
} as IPTKPRate;
32+
33+
function calcBPJSHealth(salary: number, percentage: number): number {
34+
if (salary < 12_000_000) {
35+
return salary * percentage;
36+
}
37+
return 12_000_000 * percentage;
38+
}
39+
40+
function getTax21Percentage(pkp: number): number {
41+
if (pkp <= 60_000_000) {
42+
return 0.05;
43+
} else if (pkp <= 250_000_000) {
44+
return 0.15;
45+
} else if (pkp <= 500_000_000) {
46+
return 0.25;
47+
} else if (pkp <= 5_000_000_000) {
48+
return 0.3;
49+
} else {
50+
return 0.35;
51+
}
52+
}
53+
54+
function calcPositionCost(
55+
annualizedSalary: number,
56+
positionCostPercentage: number
57+
): number {
58+
let positionCost = annualizedSalary * positionCostPercentage;
59+
console.log("calcPositionCost positionCost", positionCost);
60+
if (positionCost > 6_000_000) {
61+
positionCost = 6_000_000;
62+
} else if (positionCost > 500_000) {
63+
positionCost = 500_000;
64+
}
65+
return positionCost;
66+
}
67+
68+
function Calc({ grossSalary, maritalStatus, dependents }: CalcProps) {
69+
const annualizedSalary = grossSalary * NUMBER_OF_MONTHS_IN_A_YEAR;
70+
const ptkp = PTKP_RATE[`${maritalStatus}/${dependents}`];
71+
const pkp = annualizedSalary - PTKP_RATE[`${maritalStatus}/${dependents}`];
72+
const positionCost = calcPositionCost(
73+
annualizedSalary,
74+
POSITION_COST_PERCENTAGE
75+
);
76+
const tax21 = getTax21Percentage(pkp) * pkp - positionCost;
77+
const monthlyIncomeTax = tax21 / 12;
78+
79+
const bpjsHealthCoveredByEmployees = calcBPJSHealth(
80+
grossSalary,
81+
PERCENTAGE_OF_BPJS_HEALTH_COVERED_BY_EMPLOYEES
82+
);
83+
const bpjsHealthCoveredByCompany = calcBPJSHealth(
84+
grossSalary,
85+
PERCENTAGE_OF_BPJS_HEALTH_COVERED_BY_COMPANY
86+
);
87+
88+
const bpjsEmployementCoveredByEmployees =
89+
grossSalary * PERCENTAGE_OF_BPJS_EMPLOYEMENT_COVERED_BY_EMPLOYEES;
90+
const bpjsEmployementCoveredByCompany =
91+
grossSalary * PERCENTAGE_OF_BPJS_EMPLOYEMENT_COVERED_BY_COMPANY;
92+
93+
const bpjsEmploymentCost =
94+
bpjsEmployementCoveredByEmployees + bpjsEmployementCoveredByCompany;
95+
const bpjsHealthCost =
96+
bpjsHealthCoveredByEmployees + bpjsHealthCoveredByCompany;
97+
const nettoSalary =
98+
grossSalary - (monthlyIncomeTax + bpjsEmploymentCost + bpjsHealthCost);
99+
100+
return (
101+
<section className="w-full min-h-[300px] items-end flex flex-col gap-4">
102+
<div className="bg-[#f4f6fb] rounded-md p-4 flex flex-col w-full">
103+
<div className="flex flex-row justify-between mb-1">
104+
<h1 className="text-slate-900 text-base">Gaji Disetahunkan</h1>
105+
<h1 className="text-slate-900 text-base font-bold text-right w-[250px] md:w-fit">
106+
Rp {format(annualizedSalary)}
107+
</h1>
108+
</div>
109+
110+
<div className="flex flex-row justify-between mb-1">
111+
<p className="text-slate-500 font-thin text-xs">
112+
Penghasilan Tidak Kena Pajak (PTKP)
113+
</p>
114+
<p className="text-slate-500 font-thin text-xs text-right w-[250px] md:w-fit">
115+
Rp {format(ptkp)}
116+
</p>
117+
</div>
118+
119+
<div className="flex flex-row justify-between mb-4">
120+
<p className="text-slate-500 font-thin text-xs">
121+
Penghasilan Kena Pajak (PKP)
122+
</p>
123+
<p className="text-slate-500 font-thin text-xs text-right w-[250px] md:w-fit">
124+
Rp {format(pkp)}
125+
</p>
126+
</div>
127+
128+
<div className="h-[0.5px] bg-gray-500 w-full mb-4" />
129+
130+
<div className="flex flex-row justify-between mb-1">
131+
<h1 className="text-slate-900 text-base">
132+
Pajak Penghasilan Tahunan (PPh 21)
133+
</h1>
134+
<h1 className="text-slate-900 text-base font-bold text-right w-[250px] md:w-fit">
135+
Rp {format(tax21)}
136+
</h1>
137+
</div>
138+
139+
<div className="flex flex-row justify-between mb-2">
140+
<p className="text-slate-500 font-thin text-xs">
141+
Pajak Penghasilan Bulanan
142+
</p>
143+
<p className="text-slate-500 font-thin text-xs text-right w-[250px] md:w-fit">
144+
Rp {format(monthlyIncomeTax)}
145+
</p>
146+
</div>
147+
148+
<div className="flex flex-row justify-between mb-1">
149+
<h1 className="text-slate-900 text-base">BPJS Kesehatan</h1>
150+
<h1 className="text-slate-900 text-base font-bold text-right w-[250px] md:w-fit">
151+
Rp {format(bpjsHealthCost)}
152+
</h1>
153+
</div>
154+
155+
<div className="flex flex-row justify-between mb-2">
156+
<p className="text-slate-500 font-thin text-xs">
157+
Iuran ditanggung Karyawan
158+
</p>
159+
<p className="text-slate-500 font-thin text-xs text-right w-[250px] md:w-fit">
160+
Rp {format(bpjsHealthCoveredByEmployees)}
161+
</p>
162+
</div>
163+
164+
<div className="flex flex-row justify-between mb-2">
165+
<p className="text-slate-500 font-thin text-xs">
166+
Iuran ditanggung Perusahaan
167+
</p>
168+
<p className="text-slate-500 font-thin text-xs text-right w-[250px] md:w-fit">
169+
Rp {format(bpjsHealthCoveredByCompany)}
170+
</p>
171+
</div>
172+
173+
<div className="flex flex-row justify-between mb-1">
174+
<h1 className="text-slate-900 text-base">BPJS Ketenagakerjaan</h1>
175+
<h1 className="text-slate-900 text-base font-bold text-right w-[250px] md:w-fit">
176+
Rp {format(bpjsEmploymentCost)}
177+
</h1>
178+
</div>
179+
180+
<div className="flex flex-row justify-between mb-2">
181+
<p className="text-slate-500 font-thin text-xs">
182+
Iuran ditanggung Karyawan
183+
</p>
184+
<p className="text-slate-500 font-thin text-xs text-right w-[250px] md:w-fit">
185+
Rp {format(bpjsEmployementCoveredByEmployees)}
186+
</p>
187+
</div>
188+
189+
<div className="flex flex-row justify-between mb-2">
190+
<p className="text-slate-500 font-thin text-xs">
191+
Iuran ditanggung Perusahaan
192+
</p>
193+
<p className="text-slate-500 font-thin text-xs text-right w-[250px] md:w-fit">
194+
Rp {format(bpjsEmployementCoveredByCompany)}
195+
</p>
196+
</div>
197+
</div>
198+
199+
<div className="bg-lime-100 rounded-md p-6 flex flex-col w-full">
200+
<div className="flex flex-col">
201+
<h1 className="text-slate-900 text-sm mb-2">
202+
Gaji bersih bulanan (Take Home Pay)
203+
</h1>
204+
<h1 className="text-slate-950 font-bold text-3xl">
205+
Rp {format(nettoSalary)}
206+
</h1>
207+
</div>
208+
</div>
209+
</section>
210+
);
211+
}
212+
213+
export default Calc;

‎src/components/footer.tsx

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
function Footer() {
2+
return (
3+
<footer className="flex flex-col lg:max-w-[1100px] max-w-[100%] md:mx-auto items-center justify-center h-16 mt-8">
4+
<span>© 2023 MySaly. All rights reserved.</span>
5+
</footer>
6+
);
7+
}
8+
9+
export default Footer;

‎src/components/info.tsx

+145
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
import Collapse from "./ui/collapse";
2+
3+
function Info() {
4+
return (
5+
<section className="flex flex-col md:flex-row lg:max-w-[1200px] md:mx-auto max-w-[100%] px-6">
6+
<div className="md:min-w-[440px] mb-5 md:mb-0">
7+
<h1 className="text-center text-xl mb-2 md:mb-0 md:text-left lg:text-2xl font-semibold leading-tight tracking-tighter lg:leading-[1.1]">
8+
Informasi Seputar MySaly
9+
</h1>
10+
</div>
11+
<div className="flex-col grow">
12+
<Collapse title="MySaly itu apa ya?" wrapperClassName="pb-3">
13+
<p>
14+
MySaly adalah temanmu dalam menghitung gaji bersih bulananmu. Cukup
15+
masukkan gaji kotormu dan status perkawinan, lalu MySaly akan
16+
menghitung gaji bersihmu secara otomatis. Praktis dan mudah!
17+
</p>
18+
</Collapse>
19+
20+
<Collapse
21+
title="Kira-kira apa aja yang masuk dalam gaji ya?"
22+
wrapperClassName="pb-3 pt-3"
23+
>
24+
<ul className="list-disc list-inside ml-1">
25+
<li>Gaji Pokok</li>
26+
<li>Tunjangan Tetap</li>
27+
<li>Tunjangan Tidak Tetap</li>
28+
<li>Potongan</li>
29+
<li>Uang Lembur</li>
30+
</ul>
31+
</Collapse>
32+
33+
<Collapse title="Gaji bersih itu apa ya?" wrapperClassName="pb-3 pt-3">
34+
<p>
35+
Gaji bersih atau yang sering disebut take home pay adalah uang yang
36+
kita terima setiap bulan setelah dikurangi beberapa potongan,
37+
seperti pajak PPh 21, biaya jabatan, iuran BPJS Kesehatan, dan iuran
38+
BPJS Ketenagakerjaan.
39+
</p>
40+
</Collapse>
41+
42+
<Collapse
43+
title="Tarif PTKP yang terbaru berapa ya?"
44+
wrapperClassName="pb-3 pt-3"
45+
>
46+
<p className="mb-2">Tarif PTKP Pria/Wanita Tidak Kawin</p>
47+
<ul className="list-disc list-inside ml-1 mb-4 leading-7">
48+
<li className="mb-2">PTKP TK/0: Rp54.000.000,- per tahun.</li>
49+
<li className="mb-2">PTKP TK/1: Rp58.500.000,- per tahun.</li>
50+
<li className="mb-2">PTKP TK/2: Rp63.000.000,- per tahun.</li>
51+
<li className="mb-2">PTKP TK/3: Rp67.500.000,- per tahun.</li>
52+
</ul>
53+
54+
<p className="mb-2">Tarif PTKP Pria/Wanita Kawin</p>
55+
<ul className="list-disc list-inside ml-1 mb-4 leading-7">
56+
<li>PTKP K/0: Rp58.500.000,- per tahun.</li>
57+
<li>PTKP K/1: Rp63.000.000,- per tahun.</li>
58+
<li>PTKP K/2: Rp67.500.000,- per tahun.</li>
59+
<li>PTKP K/3: Rp72.000.000,- per tahun.</li>
60+
</ul>
61+
62+
<p className="mb-2">
63+
Tarif PTKP Penghasilan Suami dan Istri Digabung
64+
</p>
65+
<ul className="list-disc list-inside ml-1 leading-7">
66+
<li>PTKP K/I/0: Rp112.500.000,- per tahun.</li>
67+
<li>PTKP K/I/1: Rp117.000.000,- per tahun.</li>
68+
<li>PTKP K/I/2: Rp121.500.000,- per tahun.</li>
69+
<li>PTKP K/I/3: Rp126.000.000,- per tahun.</li>
70+
</ul>
71+
</Collapse>
72+
73+
<Collapse title="Apa itu Biaya jabatan?" wrapperClassName="pb-3 pt-3">
74+
<p>
75+
Biaya jabatan adalah biaya yang diperlukan untuk mengurus, memungut,
76+
dan menjaga penghasilan kita sebagai karyawan tetap, tanpa memandang
77+
jabatan kita di perusahaan.
78+
</p>
79+
<br />
80+
<p>
81+
Dalam perhitungan penghasilan yang akan dikenakan pajak, ada aturan
82+
yang perlu diikuti, yaitu sesuai dengan{" "}
83+
<a
84+
className="text-blue-600 underline"
85+
href="https://jdih.kemenkeu.go.id/fulltext/2008/250~PMK.03~2008Per.HTM#:~:text=%3A-,PERATURAN%20MENTERI%20KEUANGAN%20TENTANG%20BESARNYA%20BIAYA%20JABATAN%20ATAU%20BIAYA%20PENSIUN,BRUTO%20PEGAWAI%20TETAP%20ATAU%20PENSIUNAN.&text=Agar%20setiap%20orang%20mengetahuinya%2C%20memerintahkan,dalam%20Berita%20Negara%20Republik%20Indonesia."
86+
target="_blank"
87+
>
88+
Peraturan Menteri Keuangan (PMK) Nomor 250/PMK.03/2008
89+
</a>
90+
, yang menetapkan besarnya sebesar 5%. Jadi, jumlah biaya jabatan
91+
yang bisa kita kurangkan dari penghasilan bruto kita setiap bulan
92+
adalah 5% dari total penghasilan atau maksimal Rp6.000.000 dalam
93+
setahun, yang setara dengan Rp500.000 per bulan. Santai saja, tidak
94+
perlu terlalu pusing!
95+
</p>
96+
</Collapse>
97+
98+
<Collapse title="Berapa pajak PPh 21-nya?" wrapperClassName="pb-3 pt-3">
99+
<ul className="list-disc list-inside ml-1 mb-4 leading-7">
100+
<li>5%: Penghasilan tahunan sampai dengan Rp60.000.000,-</li>
101+
<li>
102+
15%: Penghasilan tahunan mulai Rp60.000.000,- sampai dengan
103+
Rp250.000.000,-
104+
</li>
105+
<li>
106+
25%: Penghasilan tahunan mulai dari Rp250.000.000,- sampai dengan
107+
Rp500.000.000,-
108+
</li>
109+
<li>
110+
30%: Penghasilan tahunan mulai dari Rp500.000.000,- sampai dengan
111+
Rp5.000.000.000,-
112+
</li>
113+
<li>35%: Penghasilan di atas Rp5.000.000.000,-</li>
114+
</ul>
115+
Bagi wajib pajak yang tidak memiliki NPWP, dikenakan tarif 20% lebih
116+
tinggi dari tarif normal.
117+
</Collapse>
118+
119+
<Collapse
120+
title="BPJS Kesehatan berapa ya iurannya?"
121+
wrapperClassName="pb-3 pt-3"
122+
>
123+
<p>
124+
Jadi, untuk BPJS Kesehatan, setiap bulan kamu hanya perlu bayar 1%
125+
dari gaji kamu, sementara sisanya, yaitu 4%, akan ditanggung oleh
126+
perusahaan.
127+
</p>
128+
</Collapse>
129+
130+
<Collapse
131+
title="BPJS Ketenaga Kerjaan berapa ya iurannya?"
132+
wrapperClassName="pb-3 pt-3"
133+
>
134+
<p>
135+
Iuran BPJS Ketenagakerjaan itu simpel, teman. Jadi, dari total gaji
136+
yang kamu dapetin, kamu cuma perlu bayar 2%, sementara perusahaan
137+
kamu tanggung 3,7%. Gampang, kan?
138+
</p>
139+
</Collapse>
140+
</div>
141+
</section>
142+
);
143+
}
144+
145+
export default Info;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,216 @@
1+
function CoWorkersIllustration() {
2+
return (
3+
<svg
4+
xmlns="http://www.w3.org/2000/svg"
5+
data-name="Layer 1"
6+
width="420"
7+
height="420"
8+
viewBox="0 0 715.29587 604.16998"
9+
>
10+
<path
11+
d="M705.666 206.72q1.26 4.92 2.36 9.89h-249.73v-9.89Z"
12+
fill="#e6e6e6"
13+
/>
14+
<path
15+
d="M697.706 180.65a305.612 305.612 0 0 0-14.04-33.26q-2.46-4.995-5.11-9.89c-.29-.56-.6-1.12-.91-1.68v73.6h21.58v-24.39Z"
16+
fill="#e6e6e6"
17+
/>
18+
<path
19+
d="M683.666 147.39h-6.02v-9.89h.91q2.655 4.89 5.11 9.89ZM697.706 180.65h-20.06v9.89h21.58v-5.51l-1.52-4.38z"
20+
fill="#ccc"
21+
/>
22+
<path fill="#e6e6e6" d="M644.385 124.017h21.576v85.404h-21.576z" />
23+
<path
24+
fill="#ccc"
25+
d="M644.385 137.502h21.576v9.889h-21.576zM644.385 180.653h21.576v9.889h-21.576z"
26+
/>
27+
<path
28+
d="M567.566 123.52h-77.4a4.507 4.507 0 0 0-4.5 4.5v77.4a4.656 4.656 0 0 0 .19 1.3 4.51 4.51 0 0 0 4.31 3.2h77.4a4.51 4.51 0 0 0 4.31-3.2 4.638 4.638 0 0 0 .19-1.3v-77.4a4.507 4.507 0 0 0-4.5-4.5Z"
29+
fill="#e6e6e6"
30+
/>
31+
<path
32+
d="M562.066 129.02h-66.4a4.507 4.507 0 0 0-4.5 4.5v66.4a4.507 4.507 0 0 0 4.5 4.5h66.4a4.507 4.507 0 0 0 4.5-4.5v-66.4a4.507 4.507 0 0 0-4.5-4.5Z"
33+
fill="#fff"
34+
/>
35+
<path
36+
d="M550.486 140.6h-43.24a4.507 4.507 0 0 0-4.5 4.5v43.24a4.507 4.507 0 0 0 4.5 4.5h43.24a4.523 4.523 0 0 0 4.5-4.5V145.1a4.507 4.507 0 0 0-4.5-4.5Z"
37+
fill="#ccc"
38+
/>
39+
<path
40+
d="m547.82 180.713-9.133-15.819a2 2 0 0 0-3.464 0l-6.669 11.551-8.746-15.147a2 2 0 0 0-3.464 0l-9.133 15.82-10.865 18.818h20.955l-2.076 3.596h43.461Z"
41+
fill="#fff"
42+
/>
43+
<circle cx="544.597" cy="150.088" r="6.293" fill="#fff" />
44+
<path
45+
d="M715.216 276.6q-.105-5.82-.46-11.58c-.1-1.84-.22-3.67-.37-5.5h-42.22a4.507 4.507 0 0 0-4.5 4.5v77.4a4.09 4.09 0 0 0 .13 1.05 4.504 4.504 0 0 0 4.37 3.45h38.92c.2-1.14.39-2.3.56-3.45.11-.68.22-1.37.32-2.05.24-1.63.47-3.26.68-4.89q.45-3.33.81-6.69 1.32-12.045 1.7-24.35v-.01q.135-4.635.14-9.31v-12c0-2.2-.02-4.39-.08-6.57Z"
46+
fill="#e6e6e6"
47+
/>
48+
<path
49+
d="M715.216 276.6q-.105-5.82-.46-11.58h-37.09a4.507 4.507 0 0 0-4.5 4.5v66.4a4.507 4.507 0 0 0 4.5 4.5h34.3c.24-1.63.47-3.26.68-4.89q.45-3.33.81-6.69 1.32-12.045 1.7-24.35v-.01q.135-4.635.14-9.31v-12c0-2.2-.02-4.39-.08-6.57Z"
50+
fill="#fff"
51+
/>
52+
<path
53+
d="M715.216 276.6h-25.97a4.507 4.507 0 0 0-4.5 4.5v43.24a4.507 4.507 0 0 0 4.5 4.5h24.21q1.32-12.045 1.7-24.35v-.01q.135-4.635.14-9.31v-12c0-2.2-.02-4.39-.08-6.57Z"
54+
fill="#ccc"
55+
/>
56+
<path
57+
d="M715.156 304.49q-.375 12.3-1.7 24.35-.36 3.36-.81 6.69h-15.42l2.08-3.59h-20.96l6.4-11.09 4.46-7.73 9.14-15.82a1.991 1.991 0 0 1 3.46 0l8.75 15.15Z"
58+
fill="#fff"
59+
/>
60+
<path
61+
d="M639.386 80.87h-181.09v-9.89h172.11q4.59 4.845 8.98 9.89Z"
62+
fill="#e6e6e6"
63+
/>
64+
<path
65+
d="M554.486 12.28q11.07 6.09 21.58 13.05v48.34h-21.58Z"
66+
fill="#e6e6e6"
67+
/>
68+
<path fill="#ccc" d="M554.486 44.906h21.576v9.889h-21.576z" />
69+
<path
70+
d="M609.326 50.73v22.94h-21.58V33.46q7.545 5.505 14.75 11.45 3.45 2.85 6.83 5.82Z"
71+
fill="#e6e6e6"
72+
/>
73+
<path
74+
d="M609.326 50.73v4.06h-21.58v-9.88h14.75q3.45 2.85 6.83 5.82Z"
75+
fill="#ccc"
76+
/>
77+
<path
78+
d="M532.456 1.26q8.565 3.855 16.84 8.22l-4.44 8.96-14.75 29.81-4.39 8.86-6.87 13.87-1.51 3.05-6.15-3.05-13.18-6.53 8.37-16.92h.01l4.38-8.86 14.76-29.81 4.34-8.76.05-.1Z"
79+
fill="#e6e6e6"
80+
/>
81+
<path
82+
fill="#ccc"
83+
d="m549.246 9.57-4.39 8.87-19.33-9.58 4.34-8.76.05-.1 2.54 1.26 16.79 8.31zM506.378 47.535l4.388-8.862 19.336 9.573-4.388 8.862z"
84+
/>
85+
<path
86+
d="M711.646 342.47c-.17 1.15-.36 2.31-.56 3.45-.35 2.16-.73 4.3-1.14 6.44h-251.65v-9.89Z"
87+
fill="#e6e6e6"
88+
/>
89+
<path fill="#e6e6e6" d="M554.486 259.765h21.576v85.404h-21.576z" />
90+
<path
91+
fill="#ccc"
92+
d="M554.486 273.249h21.576v9.889h-21.576zM554.486 316.401h21.576v9.889h-21.576z"
93+
/>
94+
<path fill="#e6e6e6" d="M587.748 259.765h21.576v85.404h-21.576z" />
95+
<path
96+
fill="#ccc"
97+
d="M587.748 273.249h21.576v9.889h-21.576zM587.748 316.401h21.576v9.889h-21.576z"
98+
/>
99+
<path
100+
fill="#e6e6e6"
101+
d="m498.003 335.949 37.893-76.537 19.336 9.573-37.893 76.537z"
102+
/>
103+
<path
104+
fill="#ccc"
105+
d="m525.525 280.359 4.387-8.863 19.336 9.573-4.387 8.863zM506.38 319.03l4.387-8.862 19.336 9.573-4.388 8.862z"
106+
/>
107+
<path
108+
d="M292.798 380.582a19.791 19.791 0 0 1 2.57 1.762l89.03-27.706 5.02-22.088 34.325 3.22-5.088 39.966a15.04 15.04 0 0 1-12.568 12.956L301.314 405.28a19.736 19.736 0 1 1-8.516-24.698Z"
109+
fill="#ffb8b8"
110+
/>
111+
<path d="m439.606 349.02-.59-10.08v-.01l-.16-2.76-1.74-29.68a23.473 23.473 0 0 0-21.33-25.35 23.15 23.15 0 0 0-5.34.14 23.543 23.543 0 0 0-20.15 21.18l-3.61 15.02-6.31 26.27a8.418 8.418 0 0 0 4.34 9.48 8.275 8.275 0 0 0 3.13.92l10.52.94 10.13.9 11.69 1.04.84.07 2.16.2 7.23.64h.02a8.563 8.563 0 0 0 6.62-2.36 8.58 8.58 0 0 0 1.59-2.12 8.245 8.245 0 0 0 .96-4.44Z" />
112+
<path
113+
d="M537.302 226.793c0 34.225-31.647 29.048-70.685 29.048s-70.685 5.177-70.685-29.048S411.39 131.9 466.617 131.9c57.129 0 70.685 60.667 70.685 94.893Z"
114+
fill="#2f2e41"
115+
/>
116+
<circle
117+
cx="708.732"
118+
cy="356.135"
119+
r="47.564"
120+
transform="rotate(-45 409.007 574.722)"
121+
fill="#ffb8b8"
122+
/>
123+
<path
124+
d="M533.336 275.78a29.37 29.37 0 0 0-2.91-2.46 30.92 30.92 0 0 0-22.63-5.99l-87.65 11.09a31.12 31.12 0 0 0-27.15 33.78c.25 2.8.53 5.75.84 8.82.4 3.85.85 7.89 1.37 12.07.86 7.03 1.91 14.45 3.16 22 1.09 6.5 2.33 13.09 3.75 19.6q.615 2.82 1.28 5.62a.036.036 0 0 0 .01.03c.05.23.1.46.16.68a.074.074 0 0 0 .01.05q.93 3.9 1.95 7.71c.26.94.51 1.87.77 2.79.7 2.5 1.44 4.95 2.2 7.36 5.71 17.93 13.26 33.2 23.23 41.32.13.12.26.22.4.33l.4.31.58-.18 2.46-.73 2.1-.64 27.36-8.23 6.81-2.05 21.88-6.59h.01l20.01-6.02 2.44-.74 17.19-5.17.55-6.42.74-8.52.44-5.12 3.48-40.38 2.03-23.56 2.2-25.58a31.077 31.077 0 0 0-9.47-25.18Z"
125+
fill="#2f2e41"
126+
/>
127+
<path
128+
d="M582.346 471.17c-.23-.67-.45-1.34-.68-2-10.28-30-25.44-52.69-47.75-65.05-.01 0-.01-.01-.02-.01l-.38-.21-19.44 6.73-2.4.83-2.5.87-.03.01-18.2 6.3-.03.01-14.28 4.94h-.01l-13.21 4.57h-.01l-3.07 1.07-23.09 7.99-2.68.93-2.4.83-.37.13-.07 1.14-1.65 28.92-.11 2-2.06 36.09-68.41 96.91h46.8a308.163 308.163 0 0 0 46.84-3.57l43.91-47.32a2.908 2.908 0 0 1 4.94 1.44l6.45 32a309.23 309.23 0 0 0 88-48.42c-3.08-24.85-7.58-47.5-14.09-67.13ZM411.424 203.554h18.813l8.3-23.24 1.659 23.24h8.991l4.842-13.556.968 13.556h66.812a50.35 50.35 0 0 0-50.35-50.351h-9.684a50.35 50.35 0 0 0-50.35 50.35Z"
129+
fill="#2f2e41"
130+
/>
131+
<path
132+
d="M295.71 455.557a22.788 22.788 0 0 1-31.06-16.008l-80.466-9.113 26.973-32.3 72.277 13.605a22.911 22.911 0 0 1 12.275 43.816Z"
133+
fill="#a0616a"
134+
/>
135+
<path
136+
d="m252.326 395.77-34.01-6.08-2.47-.44a14.213 14.213 0 0 1-11.11-18.09l16.7-55.49.5-1.65a32.468 32.468 0 0 0-15.23-37.51 32.277 32.277 0 0 0-47.02 20.54l-21.3 86.93-.79 3.22a43.439 43.439 0 0 0 38.17 53.6c.07.01.13.01.2.02l12.75 1.13 16.54 1.47 20.8 1.85 19.41 1.73a10.174 10.174 0 0 0 10.94-8.38c.03-.14.05-.28.07-.42l4.12-30.76.04-.28a10.17 10.17 0 0 0-8.31-11.39Z"
137+
fill="#ccc"
138+
/>
139+
<path
140+
d="M199.928 162.702c-13.577 26.692-24.277 54.224-54.224 54.224a54.224 54.224 0 0 1-54.224-54.224c0-29.948 24.279-53.85 54.224-54.225 30.435-.38 79.494 4.541 54.224 54.225ZM233.986 511.9l-1.23-12.28-.92.05-161.69 7.79.42 12.43.71 21.29a308.81 308.81 0 0 0 82.39 44.85l1.05-8.14a2.904 2.904 0 0 1 5.68-.36l3.19 11.89a305.319 305.319 0 0 0 79.59 14.41Z"
141+
fill="#2f2e41"
142+
/>
143+
<circle
144+
cx="390.724"
145+
cy="318.332"
146+
r="47.564"
147+
transform="rotate(-45 91 536.92)"
148+
fill="#a0616a"
149+
/>
150+
<path
151+
d="m243.276 500.45-9.43-29.28-.16-.5-.48-1.5-2.69-8.34c-.87-2.71-1.7-5.43-2.47-8.16q-1.065-3.69-1.99-7.4-1.32-5.16-2.4-10.36a256.725 256.725 0 0 1-4.87-35.33q-.33-4.935-.47-9.89a255.64 255.64 0 0 1 5.43-60.31 81.786 81.786 0 0 0-40.34-88.57 80.416 80.416 0 0 0-32.64-9.79c-.79-.06-1.58-.11-2.38-.16a80.962 80.962 0 0 0-64.94 26.26 82.574 82.574 0 0 0-11.32 15.68 81.446 81.446 0 0 0-9.32 51.69l11.24 75.47-2.56 31.77-3.02 37.44-.16 2-3.22 39.92a8.694 8.694 0 0 0 5.48 8.8 8.51 8.51 0 0 0 3.2.61c.16 0 .31 0 .47-.01l159.75-8.59 1.46-.08a8.704 8.704 0 0 0 7.83-11.37Z"
152+
fill="#ccc"
153+
/>
154+
<path
155+
d="M198.845 143.537a64.691 64.691 0 0 1-36.97 11.425 39.643 39.643 0 0 0 15.712 6.464 130.445 130.445 0 0 1-53.282.3 34.485 34.485 0 0 1-11.155-3.831c-3.377-2.05-6.203-5.316-6.885-9.208-1.169-6.678 4.035-12.746 9.443-16.835a69.653 69.653 0 0 1 58.532-11.696c6.539 1.69 13.089 4.545 17.335 9.796s5.505 13.346 1.46 18.753Z"
156+
fill="#2f2e41"
157+
/>
158+
<path
159+
d="M201.277 445.663a22.788 22.788 0 0 1-31.399-15.333l-80.644-7.369 26.268-32.876 72.554 12.038a22.911 22.911 0 0 1 13.221 43.54Z"
160+
fill="#a0616a"
161+
/>
162+
<path
163+
d="m156.616 386.83-18.23-2.85-18.39-2.88a14.211 14.211 0 0 1-11.5-17.84l15.96-57.5a32.469 32.469 0 0 0-16.04-37.17 31.983 31.983 0 0 0-28.61-.75 32.078 32.078 0 0 0-17.95 22.29l-20.13 90.61a43.464 43.464 0 0 0 39.51 52.78l69.63 4.67a10.185 10.185 0 0 0 10.76-8.61c.03-.14.04-.28.06-.42l3.45-30.84.03-.28a10.179 10.179 0 0 0-8.55-11.21Z"
164+
fill="#ccc"
165+
/>
166+
<path
167+
d="m465.093 339.03-4.173 2.276-51.412 28.052-4.173 2.277a8.715 8.715 0 0 0-3.472 11.811l38.285 70.168a8.715 8.715 0 0 0 11.812 3.472l.01-.005 59.737-32.594.01-.006a8.715 8.715 0 0 0 3.473-11.811l-38.285-70.168a8.715 8.715 0 0 0-11.812-3.472Z"
168+
fill="#e6e6e6"
169+
/>
170+
<path
171+
d="m466.293 341.23-4.942 2.696-49.873 27.212-4.942 2.697a6.206 6.206 0 0 0-2.473 8.41l38.285 70.169a6.206 6.206 0 0 0 8.412 2.472l.01-.005 59.736-32.594.011-.006a6.206 6.206 0 0 0 2.473-8.41l-38.285-70.169a6.206 6.206 0 0 0-8.412-2.473Z"
172+
fill="#fff"
173+
/>
174+
<path
175+
d="M467.696 361.04a1.46 1.46 0 0 0-1.97-.58l-1.99 1.09-20.29 11.07a1.452 1.452 0 0 0 1.39 2.55l22.28-12.16a1.46 1.46 0 0 0 .58-1.97ZM475.286 362.47a1.435 1.435 0 0 0-1.96-.57l-27.53 15.02a1.448 1.448 0 1 0 1.39 2.54l1.28-.7 8.01-4.37h.01l18.22-9.95a1.44 1.44 0 0 0 .58-1.97ZM445.366 386.41l-8.74-16.03a.702.702 0 0 0-.95-.27l-4.24 2.31-2.2 1.2-11.81 6.44-2.29 1.25a.702.702 0 0 0-.27.95l.56 1.03 8.18 15a.693.693 0 0 0 .94.27l14.1-7.69 2.2-1.2 4.24-2.31a.697.697 0 0 0 .28-.95ZM478.746 381.38a1.447 1.447 0 0 0-1.96-.58l-30.22 16.49a1.45 1.45 0 0 0-.73 1.53 1.211 1.211 0 0 0 .15.44 1.473 1.473 0 0 0 .29.37l.01.01a1.462 1.462 0 0 0 1.67.2l30.22-16.49a1.447 1.447 0 0 0 .57-1.97ZM486.346 382.81a1.45 1.45 0 0 0-1.97-.57l-35.47 19.35a1.415 1.415 0 0 0-.73 1.51v.01a1.21 1.21 0 0 0 .15.44 1.4 1.4 0 0 0 .3.38 1.43 1.43 0 0 0 1.67.2l35.47-19.35a1.457 1.457 0 0 0 .58-1.97Z"
176+
fill="#e6e6e6"
177+
/>
178+
<circle cx="436.444" cy="407.191" r="6.15" fill="#e6e6e6" />
179+
<path
180+
d="m483.093 312.03-4.173 2.276-51.412 28.052-4.173 2.277a8.715 8.715 0 0 0-3.472 11.811l38.285 70.168a8.715 8.715 0 0 0 11.812 3.472l.01-.005 59.737-32.594.01-.006a8.715 8.715 0 0 0 3.473-11.811l-38.285-70.168a8.715 8.715 0 0 0-11.812-3.472Z"
181+
fill="#ccc"
182+
/>
183+
<path
184+
d="m484.293 314.23-4.942 2.696-49.873 27.212-4.942 2.697a6.206 6.206 0 0 0-2.473 8.41l38.285 70.169a6.206 6.206 0 0 0 8.412 2.472l.01-.005 59.736-32.594.011-.006a6.206 6.206 0 0 0 2.473-8.41l-38.285-70.169a6.206 6.206 0 0 0-8.412-2.473Z"
185+
fill="#fff"
186+
/>
187+
<path
188+
d="M485.696 334.04a1.46 1.46 0 0 0-1.97-.58l-10.26 5.6-2.86 1.56-9.16 5a1.452 1.452 0 0 0 1.39 2.55l10.8-5.89 2.26-1.24 9.22-5.03a1.46 1.46 0 0 0 .58-1.97ZM493.286 335.47a1.435 1.435 0 0 0-1.96-.57l-14.31 7.81-2.2 1.2-11.02 6.01a1.448 1.448 0 1 0 1.39 2.54l11.02-6.01 2.2-1.2 14.3-7.81a1.44 1.44 0 0 0 .58-1.97Z"
189+
fill="#ccc"
190+
/>
191+
<path d="m463.366 359.41-6.97-12.78-1.2-2.2-.57-1.05a.702.702 0 0 0-.95-.27l-14.15 7.72-6.39 3.48a.702.702 0 0 0-.27.95l.57 1.04.62 1.15.58 1.05v.01l6.97 12.78a.693.693 0 0 0 .94.27l20.54-11.2a.697.697 0 0 0 .28-.95Z" />
192+
<path
193+
d="M496.746 354.38a1.447 1.447 0 0 0-1.96-.58l-9.03 4.93-2.2 1.2-8.72 4.75-10.27 5.61a1.452 1.452 0 0 0 1.39 2.55l18.99-10.36 2.2-1.2 9.03-4.93a1.447 1.447 0 0 0 .57-1.97ZM504.346 355.81a1.45 1.45 0 0 0-1.97-.57l-14.27 7.79-2.2 1.2-19 10.36a1.448 1.448 0 1 0 1.39 2.54l18.99-10.36 2.2-1.2 14.28-7.79a1.457 1.457 0 0 0 .58-1.97Z"
194+
fill="#ccc"
195+
/>
196+
<circle cx="454.444" cy="380.191" r="6.15" fill="#ccc" />
197+
<path
198+
d="M458.576 414.768a19.791 19.791 0 0 1 3.067.55l69.75-61.877-4.51-22.198 32.61-11.185 11.804 38.52a15.04 15.04 0 0 1-6.126 16.98l-88.674 58.22a19.736 19.736 0 1 1-17.921-19.01Z"
199+
fill="#ffb8b8"
200+
/>
201+
<path d="m579.406 325.6-19.76-37.74a23.477 23.477 0 0 0-29.22-14.54c-.22.06-.43.13-.65.21a23.527 23.527 0 0 0-14.46 29.93l7.93 41.71a8.446 8.446 0 0 0 4.08 5.74 8.251 8.251 0 0 0 3.78 1.12 8.139 8.139 0 0 0 3.23-.45l4.24-1.48 27.22-9.48 8.9-3.1a8.47 8.47 0 0 0 4.71-11.92Z" />
202+
<path d="M654 471.17H1a1 1 0 0 1 0-2h653a1 1 0 0 1 0 2Z" fill="#3f3d56" />
203+
<path
204+
d="M113.496 452.67v.5a17.515 17.515 0 0 0 10.43 16 17.232 17.232 0 0 0 7.07 1.5h161.5v-18Z"
205+
fill="#3f3d56"
206+
/>
207+
<path
208+
d="M405.256 334.04a32.593 32.593 0 0 0-11.42-13.02 31.973 31.973 0 0 0-7.15-3.54 32.375 32.375 0 0 0-10.69-1.81h-155a32.538 32.538 0 0 0-32.5 32.5v90a31.373 31.373 0 0 0 .22 3.78 32.785 32.785 0 0 0 1.14 5.54 31.94 31.94 0 0 0 2.06 5.18 32.465 32.465 0 0 0 29.08 18h155a32.488 32.488 0 0 0 32.5-32.5v-90a32.222 32.222 0 0 0-3.24-14.13Z"
209+
fill="#3f3d56"
210+
/>
211+
<circle cx="306.996" cy="393.17" r="8" fill="#fff" />
212+
</svg>
213+
);
214+
}
215+
216+
export default CoWorkersIllustration;

‎src/components/ui/collapse.tsx

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import * as React from "react";
2+
3+
import { Disclosure } from "@headlessui/react";
4+
import { ChevronUpIcon } from "@heroicons/react/20/solid";
5+
6+
interface CollapseProps {
7+
title: string;
8+
children: React.ReactNode;
9+
wrapperClassName?: string;
10+
}
11+
12+
function Collapse(props: CollapseProps) {
13+
return (
14+
<Disclosure as="div" className={`border-b border-b-gray-300 ${props.wrapperClassName || ""}`}>
15+
{({ open }) => (
16+
<>
17+
<Disclosure.Button className="flex w-full items-center justify-between text-left text-lg font-bold text-gray-900 focus:outline-none focus-visible:ring focus-visible:ring-blue-400 focus-visible:ring-opacity-75">
18+
<span>{props.title}</span>
19+
<ChevronUpIcon
20+
className={`${
21+
open ? "rotate-180 transform" : ""
22+
} h-8 w-8 text-gray-500 transition-transform`}
23+
/>
24+
</Disclosure.Button>
25+
<Disclosure.Panel className="pt-4 pb-2 text-base font-normal text-gray-900">
26+
{props.children}
27+
</Disclosure.Panel>
28+
</>
29+
)}
30+
</Disclosure>
31+
);
32+
}
33+
34+
export default Collapse;

‎src/components/ui/input-currency.tsx

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { format } from "../../utils/currency";
2+
3+
interface InputCurrencyProps {
4+
onChange: (value: number) => void;
5+
value: number;
6+
}
7+
8+
function InputCurrency(props: InputCurrencyProps) {
9+
return (
10+
<div className="relative">
11+
<span className="absolute z-10 top-2 left-3 font-normal">Rp</span>
12+
<input
13+
className="w-full block pl-9 h-10 rounded-md bg-white ring-1 ring-slate-300 px-4 font-thin outline-none transition-all duration-200 ease-in-out focus:bg-white focus:ring-2 focus:ring-blue-400"
14+
placeholder="22.000.000"
15+
onChange={(e) => {
16+
const inputValue = e.target.value;
17+
const numericInput = inputValue.replace(/[^0-9]/g, "");
18+
e.target.value = format(Number(numericInput));
19+
props.onChange(Number(numericInput));
20+
}}
21+
value={format(props.value)}
22+
/>
23+
</div>
24+
);
25+
}
26+
27+
export default InputCurrency;

‎src/index.css

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
@tailwind base;
2+
@tailwind components;
3+
@tailwind utilities;

‎src/main.tsx

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import React from 'react'
2+
import ReactDOM from 'react-dom/client'
3+
import App from './App.tsx'
4+
import './index.css'
5+
6+
ReactDOM.createRoot(document.getElementById('root')!).render(
7+
<React.StrictMode>
8+
<App />
9+
</React.StrictMode>,
10+
)

‎src/utils/currency.ts

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
function format(number: number): string {
2+
return number.toFixed(0).replace(/\d(?=(\d{3})+(?!\d))/g, "$&.");
3+
}
4+
5+
export { format };

‎src/vite-env.d.ts

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/// <reference types="vite/client" />

‎tailwind.config.js

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
/** @type {import('tailwindcss').Config} */
2+
export default {
3+
content: [
4+
"./index.html",
5+
"./src/**/*.{js,ts,jsx,tsx}",
6+
],
7+
theme: {
8+
extend: {},
9+
},
10+
plugins: [],
11+
}
12+

‎tsconfig.json

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
{
2+
"compilerOptions": {
3+
"target": "ES2020",
4+
"useDefineForClassFields": true,
5+
"lib": ["ES2020", "DOM", "DOM.Iterable"],
6+
"module": "ESNext",
7+
"skipLibCheck": true,
8+
9+
/* Bundler mode */
10+
"moduleResolution": "bundler",
11+
"allowImportingTsExtensions": true,
12+
"resolveJsonModule": true,
13+
"isolatedModules": true,
14+
"noEmit": true,
15+
"jsx": "react-jsx",
16+
17+
/* Linting */
18+
"strict": true,
19+
"noUnusedLocals": true,
20+
"noUnusedParameters": true,
21+
"noFallthroughCasesInSwitch": true
22+
},
23+
"include": ["src"],
24+
"references": [{ "path": "./tsconfig.node.json" }]
25+
}

‎tsconfig.node.json

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"compilerOptions": {
3+
"composite": true,
4+
"skipLibCheck": true,
5+
"module": "ESNext",
6+
"moduleResolution": "bundler",
7+
"allowSyntheticDefaultImports": true
8+
},
9+
"include": ["vite.config.ts"]
10+
}

‎vite.config.ts

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { defineConfig } from 'vite'
2+
import react from '@vitejs/plugin-react-swc'
3+
4+
// https://vitejs.dev/config/
5+
export default defineConfig({
6+
plugins: [react()],
7+
})

‎yarn.lock

+1,636
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)
Please sign in to comment.