Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Submit feedback
Sign in
Toggle navigation
C
ctas-box
Project overview
Project overview
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Packages
Packages
Container Registry
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Fachri
ctas-box
Commits
3fa64b2e
Commit
3fa64b2e
authored
Oct 20, 2025
by
Rais Aryaguna
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
fix error eslint and type
parent
d96c2477
Changes
28
Show whitespace changes
Inline
Side-by-side
Showing
28 changed files
with
514 additions
and
492 deletions
+514
-492
package.json
package.json
+1
-0
src/actions/master-data.ts
src/actions/master-data.ts
+3
-3
src/auth/view/jwt/jwt-sign-in-view.tsx
src/auth/view/jwt/jwt-sign-in-view.tsx
+5
-5
src/auth/view/jwt/useLogin.tsx
src/auth/view/jwt/useLogin.tsx
+8
-9
src/components/NumberFormatRupiahWithAllowedNegative/NumberFormatRupiahWithAllowedNegative.jsx
...AllowedNegative/NumberFormatRupiahWithAllowedNegative.jsx
+22
-22
src/sections/account/account-general.tsx
src/sections/account/account-general.tsx
+49
-40
src/sections/bupot-21-26/DialogPenandatangan.tsx
src/sections/bupot-21-26/DialogPenandatangan.tsx
+6
-6
src/sections/bupot-21-26/bupot-bulanan/components/rekam/Identitas.tsx
.../bupot-21-26/bupot-bulanan/components/rekam/Identitas.tsx
+1
-1
src/sections/bupot-21-26/bupot-bulanan/hooks/usePphDipotong.tsx
...ctions/bupot-21-26/bupot-bulanan/hooks/usePphDipotong.tsx
+2
-0
src/sections/bupot-21-26/bupot-bulanan/utils/api.tsx
src/sections/bupot-21-26/bupot-bulanan/utils/api.tsx
+1
-1
src/sections/bupot-21-26/bupot-bulanan/view/bulanan-rekam-view.tsx
...ons/bupot-21-26/bupot-bulanan/view/bulanan-rekam-view.tsx
+7
-7
src/sections/bupot-unifikasi/bupot-digunggung/components/rekamDigunggung/Identitas.tsx
...bupot-digunggung/components/rekamDigunggung/Identitas.tsx
+33
-38
src/sections/bupot-unifikasi/bupot-digunggung/view/digunggung-list-view.tsx
...-unifikasi/bupot-digunggung/view/digunggung-list-view.tsx
+43
-41
src/sections/bupot-unifikasi/bupot-digunggung/view/digunggungRekamView.tsx
...t-unifikasi/bupot-digunggung/view/digunggungRekamView.tsx
+62
-62
src/sections/bupot-unifikasi/bupot-dn/components/dialog/ModalCetakPdfDn.tsx
...-unifikasi/bupot-dn/components/dialog/ModalCetakPdfDn.tsx
+10
-10
src/sections/bupot-unifikasi/bupot-dn/hooks/useGetDn.tsx
src/sections/bupot-unifikasi/bupot-dn/hooks/useGetDn.tsx
+24
-17
src/sections/bupot-unifikasi/bupot-dn/hooks/usePphDipotong.tsx
...ections/bupot-unifikasi/bupot-dn/hooks/usePphDipotong.tsx
+2
-1
src/sections/bupot-unifikasi/bupot-dn/utils/api.tsx
src/sections/bupot-unifikasi/bupot-dn/utils/api.tsx
+6
-6
src/sections/bupot-unifikasi/bupot-dn/view/dn-list-view.tsx
src/sections/bupot-unifikasi/bupot-dn/view/dn-list-view.tsx
+33
-29
src/sections/bupot-unifikasi/bupot-dn/workers/normalizeDn.worker.js
...ns/bupot-unifikasi/bupot-dn/workers/normalizeDn.worker.js
+1
-0
src/sections/bupot-unifikasi/bupot-dokumen-dipersamakan/components/rekamDokumenDipersamakan/Identitas.tsx
...samakan/components/rekamDokumenDipersamakan/Identitas.tsx
+64
-64
src/sections/bupot-unifikasi/bupot-dokumen-dipersamakan/view/dokumen-dipersamakan-list-view.tsx
...umen-dipersamakan/view/dokumen-dipersamakan-list-view.tsx
+47
-45
src/sections/bupot-unifikasi/bupot-nr/view/nr-list-view.tsx
src/sections/bupot-unifikasi/bupot-nr/view/nr-list-view.tsx
+39
-40
src/sections/bupot-unifikasi/bupot-nr/view/nrRekamView.tsx
src/sections/bupot-unifikasi/bupot-nr/view/nrRekamView.tsx
+2
-2
src/sections/bupot-unifikasi/bupot-ssp/components/rekamSsp/Identitas.tsx
...pot-unifikasi/bupot-ssp/components/rekamSsp/Identitas.tsx
+33
-38
src/theme/with-settings/update-core.ts
src/theme/with-settings/update-core.ts
+7
-3
tsconfig.json
tsconfig.json
+2
-1
yarn.lock
yarn.lock
+1
-1
No files found.
package.json
View file @
3fa64b2e
...
...
@@ -121,6 +121,7 @@
"
swr
"
:
"
^2.3.4
"
,
"
turndown
"
:
"
^7.2.0
"
,
"
yet-another-react-lightbox
"
:
"
^3.25.0
"
,
"
yup
"
:
"
^1.7.1
"
,
"
zod
"
:
"
^4.0.15
"
,
"
zustand
"
:
"
^5.0.8
"
},
...
...
src/actions/master-data.ts
View file @
3fa64b2e
...
...
@@ -14,9 +14,9 @@ interface KodeNegara {
nama
:
string
;
}
interface
KodeNegaraResponse
{
data
:
KodeNegara
[];
}
//
interface KodeNegaraResponse {
//
data: KodeNegara[];
//
}
interface
UseKodeNegaraReturn
{
kodeNegara
:
KodeNegara
[];
...
...
src/auth/view/jwt/jwt-sign-in-view.tsx
View file @
3fa64b2e
import
{
useBoolean
}
from
'
minimal-shared/hooks
'
;
import
{
useEffect
,
useState
}
from
'
react
'
;
import
{
useForm
}
from
'
react-hook-form
'
;
import
*
as
yup
from
'
yup
'
;
import
*
as
z
from
'
zod
'
;
import
{
object
,
string
}
from
'
yup
'
;
import
z
from
'
zod
'
;
// import { zodResolver } from '@hookform/resolvers/zod';
import
{
yupResolver
}
from
'
@hookform/resolvers/yup
'
;
...
...
@@ -44,9 +44,9 @@ export const SignInSchema = z.object({
/**
* Form Validation Schema
*/
const
schema
=
yup
.
object
().
shape
({
email
:
yup
.
string
().
email
(
'
You must enter a valid email
'
).
required
(
'
You must enter a email
'
),
password
:
yup
.
string
().
required
(
'
Please enter your password.
'
),
const
schema
=
object
().
shape
({
email
:
string
().
email
(
'
You must enter a valid email
'
).
required
(
'
You must enter a email
'
),
password
:
string
().
required
(
'
Please enter your password.
'
),
});
export
function
JwtSignInView
()
{
...
...
src/auth/view/jwt/useLogin.tsx
View file @
3fa64b2e
import
axios
from
'
axios
'
;
import
{
useMutation
}
from
'
@tanstack/react-query
'
;
// import { submitLogin } from 'app/auth/store/loginSlice';
import
{
useDispatch
}
from
'
react-redux
'
;
import
{
useNavigate
}
from
'
react-router
'
;
// import { useLocation, useHistory } from 'react-router-dom';
import
{
submitLogin
}
from
'
src/auth/store/loginSlice
'
;
import
{
endpoints
}
from
'
src/lib/axios
'
;
//
import { endpoints } from 'src/lib/axios';
import
type
{
AppDispatch
}
from
'
src/store.tsx
'
;
const
getUserInfo
=
async
(
token
:
any
)
=>
{
axios
.
defaults
.
headers
.
common
.
Authorization
=
`Bearer
${
window
.
atob
(
token
)}
`
;
const
res
=
await
axios
.
post
(
endpoints
.
auth
.
me
);
const
{
data
}
=
await
res
.
data
;
//
const getUserInfo = async (token: any) => {
//
axios.defaults.headers.common.Authorization = `Bearer ${window.atob(token)}`;
//
const res = await axios.post(endpoints.auth.me);
//
const { data } = await res.data;
return
data
.
user
;
};
//
return data.user;
//
};
interface
SignInParams
{
email
:
string
;
...
...
src/components/NumberFormatRupiahWithAllowedNegative/NumberFormatRupiahWithAllowedNegative.jsx
View file @
3fa64b2e
...
...
@@ -3,28 +3,28 @@ import { forwardRef, useEffect, useState } from 'react';
// import { NumberFormat } from 'react-number-format';
import
{
NumericFormat
}
from
'
react-number-format
'
;
const
formatNegativeValue
=
(
value
,
negativeMask
)
=>
{
if
(
!
value
||
value
===
'
0
'
)
return
value
;
const
numValue
=
parseFloat
(
value
);
if
(
numValue
>=
0
)
return
value
;
const
formattedAbsValue
=
new
Intl
.
NumberFormat
(
'
id-ID
'
,
{
minimumFractionDigits
:
0
,
maximumFractionDigits
:
2
,
}).
format
(
Math
.
abs
(
numValue
));
switch
(
negativeMask
)
{
case
'
prefix
'
:
return
`-
${
formattedAbsValue
}
`
;
case
'
suffix
'
:
return
`
${
formattedAbsValue
}
-`
;
case
'
both
'
:
return
`(
${
formattedAbsValue
}
)`
;
default
:
return
`-
${
formattedAbsValue
}
`
;
}
};
//
const formatNegativeValue = (value, negativeMask) => {
//
if (!value || value === '0') return value;
//
const numValue = parseFloat(value);
//
if (numValue >= 0) return value;
//
const formattedAbsValue = new Intl.NumberFormat('id-ID', {
//
minimumFractionDigits: 0,
//
maximumFractionDigits: 2,
//
}).format(Math.abs(numValue));
//
switch (negativeMask) {
//
case 'prefix':
//
return `-${formattedAbsValue}`;
//
case 'suffix':
//
return `${formattedAbsValue}-`;
//
case 'both':
//
return `(${formattedAbsValue})`;
//
default:
//
return `-${formattedAbsValue}`;
//
}
//
};
export
const
getIntegerDigitCount
=
(
value
)
=>
{
if
(
!
value
)
return
0
;
...
...
src/sections/account/account-general.tsx
View file @
3fa64b2e
import
*
as
z
from
'
zod
'
;
import
z
from
'
zod
'
;
import
{
useForm
}
from
'
react-hook-form
'
;
import
{
zodResolver
}
from
'
@hookform/resolvers/zod
'
;
import
{
isValidPhoneNumber
}
from
'
react-phone-number-input/input
'
;
...
...
@@ -8,7 +8,6 @@ import Card from '@mui/material/Card';
import
Grid
from
'
@mui/material/Grid
'
;
import
Typography
from
'
@mui/material/Typography
'
;
import
{
toast
}
from
'
src/components/snackbar
'
;
import
{
Form
,
Field
,
schemaUtils
}
from
'
src/components/hook-form
'
;
...
...
@@ -30,9 +29,8 @@ export const UpdateUserSchema = z.object({
// ----------------------------------------------------------------------
export
function
AccountGeneral
()
{
const
user
=
useSelector
((
state
:
RootState
)
=>
state
.
user
);
console
.
log
(
user
)
console
.
log
(
user
)
;
const
currentUser
:
User
=
{
customer_name
:
user
?.
data
.
customer_name
,
...
...
@@ -57,7 +55,7 @@ export function AccountGeneral() {
const
{
handleSubmit
,
formState
:
{
isSubmitting
},
//
formState: { isSubmitting },
}
=
methods
;
const
onSubmit
=
handleSubmit
(
async
(
data
)
=>
{
...
...
@@ -70,47 +68,53 @@ export function AccountGeneral() {
}
});
const
styleCustom
=
{
"
& .MuiInputBase-input.Mui-disabled
"
:
{
WebkitTextFillColor
:
"
#000 !important
"
,
color
:
"
#000 !important
"
,
const
styleCustom
=
{
'
& .MuiInputBase-input.Mui-disabled
'
:
{
WebkitTextFillColor
:
'
#000 !important
'
,
color
:
'
#000 !important
'
,
},
"
& .MuiInputLabel-root.Mui-disabled
"
:
{
color
:
"
#000 !important
"
,
'
& .MuiInputLabel-root.Mui-disabled
'
:
{
color
:
'
#000 !important
'
,
},
"
& .MuiOutlinedInput-root.Mui-disabled fieldset
"
:
{
borderColor
:
"
#403535ff
"
,
}
}
'
& .MuiOutlinedInput-root.Mui-disabled fieldset
'
:
{
borderColor
:
'
#403535ff
'
,
},
};
return
(
<
Form
methods=
{
methods
}
onSubmit=
{
onSubmit
}
>
<
Box
sx=
{
{
mb
:
3
,
}
}
>
<
Typography
variant=
'h4'
>
User Profile
</
Typography
>
<
Box
sx=
{
{
mb
:
3
,
}
}
>
<
Typography
variant=
"h4"
>
User Profile
</
Typography
>
</
Box
>
<
Grid
container
spacing=
{
3
}
sx=
{
{
flexDirection
:
"
column
"
}
}
>
<
Grid
>
<
Grid
container
spacing=
{
3
}
sx=
{
{
flexDirection
:
'
column
'
}
}
>
<
Grid
>
<
Card
sx=
{
{
p
:
2
,
background
:
"
linear-gradient(to right, #143b88, #1976D2)
"
,
borderRadius
:
"
16px
"
,
mb
:
2
p
:
2
,
background
:
'
linear-gradient(to right, #143b88, #1976D2)
'
,
borderRadius
:
'
16px
'
,
mb
:
2
,
}
}
>
<
Typography
sx=
{
{
fontSize
:
'
24px
'
,
color
:
'
#fff
'
,
pb
:
'
4px
'
,
}
}
>
<
Typography
sx=
{
{
fontSize
:
"
24px
"
,
color
:
"
#fff
"
,
pb
:
"
4px
"
,
}
}
>
{
user
.
data
.
customer_name
}
</
Typography
>
<
Typography
sx=
{
{
fontSize
:
"
16
"
,
color
:
"
#BBDEFB
"
}
}
>
<
Typography
sx=
{
{
fontSize
:
'
16
'
,
color
:
'
#BBDEFB
'
,
}
}
>
{
user
.
data
.
email
}
</
Typography
>
</
Card
>
...
...
@@ -128,9 +132,14 @@ export function AccountGeneral() {
>
<
Field
.
Text
sx=
{
styleCustom
}
name=
"customer_name"
label=
"Name"
disabled
/>
<
Field
.
Text
sx=
{
styleCustom
}
name=
"email"
label=
"Email address"
disabled
/>
<
Field
.
Text
sx=
{
styleCustom
}
name=
"phone"
type=
'number'
label=
"Phone Number"
disabled
/>
<
Field
.
Text
sx=
{
styleCustom
}
name=
"address"
label=
"Address"
disabled
/>
<
Field
.
Text
sx=
{
styleCustom
}
name=
"phone"
type=
"number"
label=
"Phone Number"
disabled
/>
<
Field
.
Text
sx=
{
styleCustom
}
name=
"address"
label=
"Address"
disabled
/>
</
Box
>
</
Card
>
</
Grid
>
...
...
src/sections/bupot-21-26/DialogPenandatangan.tsx
View file @
3fa64b2e
...
...
@@ -42,12 +42,12 @@ export default function DialogPenandatangan({
// { label: 'Wakil/Kuasa', value: 1 },
// ];
const
handleSubmitLocal
=
(
data
:
any
)
=>
{
//
const handleSubmitLocal = (data: any) => {
// if (isCountDown)
// setCountdown(30); // start countdown saat submit
// else setCountdown(null);
// onSubmit(data); // tetap panggil props onSubmit
};
//
};
return
(
<
Dialog
...
...
src/sections/bupot-21-26/bupot-bulanan/components/rekam/Identitas.tsx
View file @
3fa64b2e
...
...
@@ -13,7 +13,7 @@ type IdentitasProps = {
};
const
Identitas
=
({
isPengganti
}:
IdentitasProps
)
=>
{
const
{
dnId
}
=
useParams
();
//
const { dnId } = useParams();
const
{
setValue
,
watch
}
=
useFormContext
();
const
tanggalPemotongan
=
watch
(
'
tglPemotongan
'
);
const
fgKaryawanAsing
=
watch
(
'
fgKaryawanAsing
'
);
...
...
src/sections/bupot-21-26/bupot-bulanan/hooks/usePphDipotong.tsx
View file @
3fa64b2e
/* eslint-disable @typescript-eslint/no-shadow */
import
{
useEffect
}
from
'
react
'
;
import
{
useFormContext
,
useWatch
}
from
'
react-hook-form
'
;
import
type
{
TGetListDataKOPDn
}
from
'
../types/types
'
;
...
...
@@ -66,6 +67,7 @@ const usePphDipotong = (kodeObjekPajakSelected?: TGetListDataKOPDn) => {
Number
(
handlerSetPphDipotong
[
4
])
);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
},
[
handlerSetPphDipotong
]);
return
{
...
...
src/sections/bupot-21-26/bupot-bulanan/utils/api.tsx
View file @
3fa64b2e
...
...
@@ -24,7 +24,7 @@ bulananApi.getList = async (config: any) => {
throw
new
Error
(
response
?.
message
||
'
Failed to fetch bulanan data
'
);
}
const
{
me
ssage
,
me
taPage
,
data
}
=
response
;
const
{
metaPage
,
data
}
=
response
;
return
{
total
:
metaPage
?
Number
(
metaPage
.
totalRow
)
:
0
,
data
};
};
...
...
src/sections/bupot-21-26/bupot-bulanan/view/bulanan-rekam-view.tsx
View file @
3fa64b2e
...
...
@@ -49,7 +49,7 @@ const bulananSchema = z
label
:
z
.
string
(),
value
:
z
.
string
(),
})
.
refine
((
data
)
=>
data
.
value
!==
''
,
{
.
refine
((
data
:
any
)
=>
data
.
value
!==
''
,
{
message
:
'
Kode Objek Pajak wajib diisi
'
,
}),
jenisHitung
:
z
.
string
().
optional
(),
...
...
@@ -58,7 +58,7 @@ const bulananSchema = z
label
:
z
.
string
(),
value
:
z
.
string
(),
})
.
refine
((
data
)
=>
data
.
value
!==
''
,
{
.
refine
((
data
:
any
)
=>
data
.
value
!==
''
,
{
message
:
'
Fasilitas wajib dipilih
'
,
}),
fgSkb
:
z
.
boolean
().
optional
(),
...
...
@@ -73,7 +73,7 @@ const bulananSchema = z
label
:
z
.
string
(),
value
:
z
.
string
(),
})
.
refine
((
data
)
=>
data
.
value
!==
''
,
{
.
refine
((
data
:
any
)
=>
data
.
value
!==
''
,
{
message
:
'
PTKP wajib dipilih
'
,
}),
email
:
z
.
string
().
optional
(),
...
...
@@ -84,7 +84,7 @@ const bulananSchema = z
label
:
z
.
string
(),
value
:
z
.
string
(),
})
.
refine
((
data
)
=>
data
.
value
!==
''
,
{
.
refine
((
data
:
any
)
=>
data
.
value
!==
''
,
{
message
:
'
NITKU Pemotong wajib diisi
'
,
}),
tglPemotongan
:
z
.
string
().
min
(
1
,
'
Tanggal Pemotongan wajib diisi
'
),
...
...
@@ -142,11 +142,11 @@ const bulananSchema = z
path
:
[
'
skb
'
],
}
)
.
refine
((
data
)
=>
parseInt
(
data
.
phBruto
||
'
0
'
,
10
)
>=
0
,
{
.
refine
((
data
:
any
)
=>
parseInt
(
data
.
phBruto
||
'
0
'
,
10
)
>=
0
,
{
message
:
'
Jumlah Penghasilan Bruto tidak boleh minus
'
,
path
:
[
'
phBruto
'
],
})
.
refine
((
data
)
=>
parseInt
(
data
.
tarif
||
'
0
'
,
10
)
<=
100
,
{
.
refine
((
data
:
any
)
=>
parseInt
(
data
.
tarif
||
'
0
'
,
10
)
<=
100
,
{
message
:
'
Tarif tidak boleh lebih dari 100
'
,
path
:
[
'
tarif
'
],
})
...
...
@@ -168,7 +168,7 @@ const bulananSchema = z
);
export
const
BulananRekamView
=
()
=>
{
const
{
id
}
=
useParams
();
//
const { id } = useParams();
const
pathname
=
usePathname
();
const
{
mutate
:
saveBulanan
,
isPending
:
isSaving
}
=
useSaveBulanan
();
...
...
src/sections/bupot-unifikasi/bupot-digunggung/components/rekamDigunggung/Identitas.tsx
View file @
3fa64b2e
import
Grid
from
'
@mui/material/Grid
'
;
import
{
useState
}
from
'
react
'
;
import
{
useFormContext
}
from
'
react-hook-form
'
;
// import { useParams } from 'react-router';
import
{
Field
}
from
'
src/components/hook-form
'
;
...
...
@@ -10,30 +8,29 @@ type IdentitasProps = {
// disabledHapus: boolean;
};
const
Identitas
=
({
isPengganti
}:
IdentitasProps
)
=>
{
const
Identitas
=
({
isPengganti
}:
IdentitasProps
)
=>
(
// const { dnId } = useParams();
const
{
setValue
}
=
useFormContext
();
//
const { setValue } = useFormContext();
const
[
jumlahKeterangan
,
setJumlahKeterangan
]
=
useState
<
number
>
(
0
);
//
const [jumlahKeterangan, setJumlahKeterangan] = useState<number>(0);
const
maxKeterangan
=
5
;
//
const maxKeterangan = 5;
const
handleTambah
=
()
=>
{
if
(
jumlahKeterangan
<
maxKeterangan
)
{
setJumlahKeterangan
(
jumlahKeterangan
+
1
);
}
};
//
const handleTambah = () => {
//
if (jumlahKeterangan < maxKeterangan) {
//
setJumlahKeterangan(jumlahKeterangan + 1);
//
}
//
};
const
handleHapus
=
()
=>
{
if
(
jumlahKeterangan
>
0
)
{
const
newCount
=
jumlahKeterangan
-
1
;
setJumlahKeterangan
(
newCount
);
// reset value form field yang dihapus
setValue
(
`keterangan
${
newCount
+
1
}
`
,
null
);
}
};
//
const handleHapus = () => {
//
if (jumlahKeterangan > 0) {
//
const newCount = jumlahKeterangan - 1;
//
setJumlahKeterangan(newCount);
//
// reset value form field yang dihapus
//
setValue(`keterangan${newCount + 1}`, null);
//
}
//
};
return
(
<
Grid
container
rowSpacing=
{
2
}
alignItems=
"center"
columnSpacing=
{
2
}
>
<
Grid
size=
{
{
md
:
6
}
}
>
<
Field
.
DatePicker
name=
"tglPemotongan"
label=
"Tanggal Pemotongan"
/>
...
...
@@ -45,7 +42,5 @@ const Identitas = ({ isPengganti }: IdentitasProps) => {
<
Field
.
DatePicker
name=
"msPajak"
label=
"Masa Pajak"
view=
"month"
format=
"MM"
/>
</
Grid
>
</
Grid
>
);
};
);
export
default
Identitas
;
src/sections/bupot-unifikasi/bupot-digunggung/view/digunggung-list-view.tsx
View file @
3fa64b2e
...
...
@@ -74,7 +74,7 @@ export function DigunggungListView() {
.
join
(
'
AND
'
);
};
const
{
data
,
isLoading
,
isError
}
=
useGetDn
({
const
{
data
,
isLoading
}
=
useGetDn
({
params
:
{
Page
:
paginationModel
.
page
+
1
,
// API biasanya 1-based
Limit
:
paginationModel
.
pageSize
,
...
...
@@ -82,7 +82,9 @@ export function DigunggungListView() {
sortingMode
:
sortModel
[
0
]?.
field
,
sortingMethod
:
sortModel
[
0
]?.
sort
,
},
option
:
{
refetchOnWindowFocus
:
false
,
},
});
const
totalRows
=
data
?.
total
||
0
;
...
...
src/sections/bupot-unifikasi/bupot-digunggung/view/digunggungRekamView.tsx
View file @
3fa64b2e
...
...
@@ -38,9 +38,9 @@ const DigunggungRekamView = () => {
const
[
isOpenPanduan
,
setIsOpenPanduan
]
=
useState
<
boolean
>
(
false
);
const
[
isCheckedAgreement
,
setIsCheckedAgreement
]
=
useState
<
boolean
>
(
false
);
const
{
data
,
isLoading
,
isError
}
=
useGetKodeObjekPajak
();
const
{
data
}
=
useGetKodeObjekPajak
();
type
BpuFormData
=
z
.
infer
<
typeof
bpuSchema
>
;
//
type BpuFormData = z.infer<typeof bpuSchema>;
const
handleOpenPanduan
=
()
=>
setIsOpenPanduan
(
!
isOpenPanduan
);
...
...
@@ -66,11 +66,11 @@ const DigunggungRekamView = () => {
defaultValues
,
});
const
{
reset
,
handleSubmit
,
formState
:
{
isSubmitting
},
}
=
methods
;
//
const {
//
reset,
//
handleSubmit,
//
formState: { isSubmitting },
//
} = methods;
const
SubmitRekam
=
()
=>
{
console
.
log
(
'
Submit API
'
);
...
...
src/sections/bupot-unifikasi/bupot-dn/components/dialog/ModalCetakPdfDn.tsx
View file @
3fa64b2e
...
...
@@ -13,16 +13,16 @@ interface ModalCetakPdfDnProps {
onClose
:
()
=>
void
;
}
const
formatTanggalIndo
=
(
isoDate
:
string
|
undefined
|
null
):
string
=>
{
if
(
!
isoDate
)
return
''
;
const
date
=
new
Date
(
isoDate
);
const
formatter
=
new
Intl
.
DateTimeFormat
(
'
id-ID
'
,
{
day
:
'
2-digit
'
,
month
:
'
long
'
,
year
:
'
numeric
'
,
});
return
formatter
.
format
(
date
);
};
//
const formatTanggalIndo = (isoDate: string | undefined | null): string => {
//
if (!isoDate) return '';
//
const date = new Date(isoDate);
//
const formatter = new Intl.DateTimeFormat('id-ID', {
//
day: '2-digit',
//
month: 'long',
//
year: 'numeric',
//
});
//
return formatter.format(date);
//
};
const
ModalCetakPdfDn
:
React
.
FC
<
ModalCetakPdfDnProps
>
=
({
payload
,
isOpen
,
onClose
})
=>
{
const
[
pdfUrl
,
setPdfUrl
]
=
useState
<
string
|
null
>
(
null
);
...
...
src/sections/bupot-unifikasi/bupot-dn/hooks/useGetDn.tsx
View file @
3fa64b2e
import
{
isEmpty
}
from
'
lodash
'
;
import
{
useQuery
}
from
'
@tanstack/react-query
'
;
import
{
useQuery
,
type
UseQueryOptions
}
from
'
@tanstack/react-query
'
;
import
dnApi
from
'
../utils/api
'
;
import
type
{
TGetListDataTableDn
,
TGetListDataTableDnResult
}
from
'
../types/types
'
;
import
{
FG_PDF_STATUS
,
FG_SIGN_STATUS
}
from
'
../constant
'
;
...
...
@@ -93,22 +93,22 @@ const normalisePropsGetDn = (params: TGetListDataTableDn) => ({
});
// ---------- normalizer for params request ----------
const
normalisPropsParmasGetDn
=
(
params
:
any
)
=>
{
const
sorting
=
!
isEmpty
(
params
.
sortModel
)
?
transformSortModelToSortApiPayload
(
params
.
sortModel
)
:
{};
//
const normalisPropsParmasGetDn = (params: any) => {
//
const sorting = !isEmpty(params.sortModel)
//
? transformSortModelToSortApiPayload(params.sortModel)
//
: {};
return
{
...
params
,
page
:
(
typeof
params
.
page
===
'
number
'
?
params
.
page
:
0
)
+
1
,
limit
:
params
.
pageSize
,
masaPajak
:
params
.
msPajak
||
null
,
tahunPajak
:
params
.
thnPajak
||
null
,
npwp
:
params
.
idDipotong
||
null
,
advanced
:
isEmpty
(
params
.
advanced
)
?
undefined
:
params
.
advanced
,
...
sorting
,
};
};
//
return {
//
...params,
//
page: (typeof params.page === 'number' ? params.page : 0) + 1,
//
limit: params.pageSize,
//
masaPajak: params.msPajak || null,
//
tahunPajak: params.thnPajak || null,
//
npwp: params.idDipotong || null,
//
advanced: isEmpty(params.advanced) ? undefined : params.advanced,
//
...sorting,
//
};
//
};
const
normalizeParams
=
(
params
:
any
)
=>
{
const
{
...
...
@@ -155,7 +155,13 @@ const normalizeParams = (params: any) => {
};
};
export
const
useGetDn
=
({
params
}:
{
params
:
any
})
=>
{
export
const
useGetDn
=
({
params
,
option
,
}:
{
params
:
any
;
option
?:
Omit
<
UseQueryOptions
,
'
queryKey
'
|
'
queryFn
'
>
;
})
=>
{
const
{
page
,
limit
,
advanced
,
sortingMode
,
sortingMethod
}
=
params
;
const
normalized
=
normalizeParams
(
params
);
...
...
@@ -227,6 +233,7 @@ export const useGetDn = ({ params }: { params: any }) => {
staleTime
:
0
,
gcTime
:
0
,
retry
:
false
,
// ...option,
});
};
...
...
src/sections/bupot-unifikasi/bupot-dn/hooks/usePphDipotong.tsx
View file @
3fa64b2e
/* eslint-disable @typescript-eslint/no-shadow */
import
{
useEffect
}
from
'
react
'
;
import
{
useFormContext
,
useWatch
}
from
'
react-hook-form
'
;
import
type
{
TGetListDataKOPDn
}
from
'
../types/types
'
;
...
...
@@ -35,7 +36,6 @@ const usePphDipotong = (kodeObjekPajakSelected?: TGetListDataKOPDn) => {
name
:
[
'
thnPajak
'
,
'
fgFasilitas
'
,
'
fgIdDipotong
'
,
'
jmlBruto
'
,
'
tarif
'
],
});
const
calculateAndSetPphDipotong
=
(
thnPajak
:
number
,
fgFasilitas
:
string
,
...
...
@@ -67,6 +67,7 @@ const usePphDipotong = (kodeObjekPajakSelected?: TGetListDataKOPDn) => {
Number
(
handlerSetPphDipotong
[
4
])
);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
},
[
handlerSetPphDipotong
]);
return
{
...
...
src/sections/bupot-unifikasi/bupot-dn/utils/api.tsx
View file @
3fa64b2e
...
...
@@ -6,8 +6,8 @@ import type {
TDeleteDnRequest
,
TGetListDataKOPDnResult
,
TGetListDataTableDnResult
,
TPostDnRequest
}
from
'
../types/types
'
;
TPostDnRequest
,
}
from
'
../types/types
'
;
import
unifikasiClient
from
'
./unifikasiClient
'
;
...
...
@@ -24,7 +24,7 @@ const axiosCetakPdf = axios.create({
// API untuk get list table
dnApi
.
getDn
=
async
(
config
:
any
)
=>
{
const
{
data
:
{
status
,
message
,
metaPage
,
data
},
data
:
{
message
,
metaPage
,
data
},
status
:
statusCode
,
}
=
await
unifikasiClient
.
get
<
TBaseResponseAPI
<
TGetListDataTableDnResult
>
>
('IF_TXR_028/bpu',
{
...
config
,
...
...
@@ -54,8 +54,8 @@ dnApi.getKodeObjekPajakDn = async (params?: Record<string, any>) => {
dnApi.saveDn = async (config: TPostDnRequest) =
>
{
const
{
data
:
{
status
,
message
,
data
,
code
},
status
:
statusCode
,
data
:
{
message
,
data
,
code
},
//
status: statusCode,
}
=
await
unifikasiClient
.
post
<
TBaseResponseAPI
<
TPostDnRequest
>>
(
'
/IF_TXR_028/bpu
'
,
{
...
config
,
});
...
...
@@ -111,7 +111,7 @@ dnApi.deleteDn = async (payload: TDeleteDnRequest, config?: Record<string, any>)
dnApi.cancel = async (
{
id
,
tglPembatalan
}
: TCancelDnRequest): Promise
<
TCancelDnResponse
>
=
>
{
const
{
data
:
{
status
,
message
,
data
,
code
,
time
,
metaPage
,
total
},
status
:
statusCode
,
//
status: statusCode,
}
=
await
unifikasiClient
.
post
(
'
/IF_TXR_028/bpu/batal
'
,
{
id
,
tglPembatalan
,
...
...
src/sections/bupot-unifikasi/bupot-dn/view/dn-list-view.tsx
View file @
3fa64b2e
...
...
@@ -276,35 +276,35 @@ export function DnListView() {
);
// --- selection helpers (kept same)
const
normalizeSelectionToArray
=
(
raw
:
unknown
):
GridRowId
[]
=>
{
if
(
!
raw
)
return
[];
if
(
typeof
raw
===
'
object
'
&&
raw
!==
null
&&
'
ids
'
in
(
raw
as
any
))
{
const
ids
=
(
raw
as
any
).
ids
;
if
(
ids
instanceof
Set
)
return
Array
.
from
(
ids
)
as
GridRowId
[];
if
(
Array
.
isArray
(
ids
))
return
ids
as
GridRowId
[];
if
(
ids
instanceof
Map
)
return
Array
.
from
((
ids
as
Map
<
any
,
any
>
).
keys
())
as
GridRowId
[];
if
(
typeof
ids
===
'
object
'
&&
ids
!==
null
)
return
Object
.
keys
(
ids
)
as
GridRowId
[];
}
if
(
Array
.
isArray
(
raw
))
return
raw
as
GridRowId
[];
if
(
raw
instanceof
Set
)
return
Array
.
from
(
raw
)
as
GridRowId
[];
if
(
raw
instanceof
Map
)
return
Array
.
from
((
raw
as
Map
<
any
,
any
>
).
keys
())
as
GridRowId
[];
if
(
typeof
raw
===
'
object
'
&&
raw
!==
null
)
{
const
obj
=
raw
as
Record
<
string
,
any
>
;
const
keys
=
Object
.
keys
(
obj
).
filter
((
k
)
=>
!!
obj
[
k
]);
if
(
keys
.
length
)
return
keys
as
GridRowId
[];
}
//
const normalizeSelectionToArray = (raw: unknown): GridRowId[] => {
//
if (!raw) return [];
//
if (typeof raw === 'object' && raw !== null && 'ids' in (raw as any)) {
//
const ids = (raw as any).ids;
//
if (ids instanceof Set) return Array.from(ids) as GridRowId[];
//
if (Array.isArray(ids)) return ids as GridRowId[];
//
if (ids instanceof Map) return Array.from((ids as Map<any, any>).keys()) as GridRowId[];
//
if (typeof ids === 'object' && ids !== null) return Object.keys(ids) as GridRowId[];
//
}
//
if (Array.isArray(raw)) return raw as GridRowId[];
//
if (raw instanceof Set) return Array.from(raw) as GridRowId[];
//
if (raw instanceof Map) return Array.from((raw as Map<any, any>).keys()) as GridRowId[];
//
if (typeof raw === 'object' && raw !== null) {
//
const obj = raw as Record<string, any>;
//
const keys = Object.keys(obj).filter((k) => !!obj[k]);
//
if (keys.length) return keys as GridRowId[];
//
}
try
{
if
((
raw
as
any
)[
Symbol
.
iterator
])
{
return
Array
.
from
(
raw
as
Iterable
<
unknown
>
)
as
GridRowId
[];
}
}
catch
{
/* ignore */
}
//
try {
//
if ((raw as any)[Symbol.iterator]) {
//
return Array.from(raw as Iterable<unknown>) as GridRowId[];
//
}
//
} catch {
//
/* ignore */
//
}
return
[];
};
//
return [];
//
};
const
getSelectedRowByKey
=
(
key
?:
GridRowId
|
'
all
'
)
=>
{
const
api
=
apiRef
.
current
;
...
...
@@ -381,6 +381,7 @@ export function DnListView() {
canReplacement
:
count
===
1
&&
dataSelected
[
0
].
fgStatus
===
FG_STATUS_DN
.
NORMAL_DONE
,
canCancel
:
hasSelection
&&
allNormal
,
};
// eslint-disable-next-line react-hooks/exhaustive-deps
},
[
selectionVersion
]);
useEffect
(()
=>
{
...
...
@@ -467,6 +468,7 @@ export function DnListView() {
},
],
],
// eslint-disable-next-line react-hooks/exhaustive-deps
[
validatedActions
,
refetch
,
handleEditData
]
);
...
...
@@ -492,10 +494,12 @@ export function DnListView() {
const
api
=
apiRef
.
current
;
if
(
!
api
)
return
;
const
id
=
window
.
setTimeout
(()
=>
{
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const
selected
=
getSelectedRowByKey
(
'
all
'
);
},
100
);
// eslint-disable-next-line consistent-return
return
()
=>
clearTimeout
(
id
);
// eslint-disable-next-line react-hooks/exhaustive-deps
},
[
apiRef
,
selectionVersion
]);
return
(
...
...
src/sections/bupot-unifikasi/bupot-dn/workers/normalizeDn.worker.js
View file @
3fa64b2e
...
...
@@ -48,6 +48,7 @@ function normalisePropsGetDn(params) {
};
}
// eslint-disable-next-line func-names
onmessage
=
function
(
e
)
{
const
{
data
}
=
e
;
// data should be array of items
...
...
src/sections/bupot-unifikasi/bupot-dokumen-dipersamakan/components/rekamDokumenDipersamakan/Identitas.tsx
View file @
3fa64b2e
import
Grid
from
'
@mui/material/Grid
'
;
import
MenuItem
from
'
@mui/material/MenuItem
'
;
import
{
useState
}
from
'
react
'
;
import
{
useFormContext
}
from
'
react-hook-form
'
;
import
{
useParams
}
from
'
react-router
'
;
//
import { useState } from 'react';
//
import { useFormContext } from 'react-hook-form';
//
import { useParams } from 'react-router';
import
{
Field
}
from
'
src/components/hook-form
'
;
type
IdentitasProps
=
{
...
...
@@ -12,27 +12,27 @@ type IdentitasProps = {
};
const
Identitas
=
({
isPengganti
}:
IdentitasProps
)
=>
{
const
{
dnId
}
=
useParams
();
const
{
setValue
}
=
useFormContext
();
//
const { dnId } = useParams();
//
const { setValue } = useFormContext();
const
[
jumlahKeterangan
,
setJumlahKeterangan
]
=
useState
<
number
>
(
0
);
//
const [jumlahKeterangan, setJumlahKeterangan] = useState<number>(0);
const
maxKeterangan
=
5
;
//
const maxKeterangan = 5;
const
handleTambah
=
()
=>
{
if
(
jumlahKeterangan
<
maxKeterangan
)
{
setJumlahKeterangan
(
jumlahKeterangan
+
1
);
}
};
//
const handleTambah = () => {
//
if (jumlahKeterangan < maxKeterangan) {
//
setJumlahKeterangan(jumlahKeterangan + 1);
//
}
//
};
const
handleHapus
=
()
=>
{
if
(
jumlahKeterangan
>
0
)
{
const
newCount
=
jumlahKeterangan
-
1
;
setJumlahKeterangan
(
newCount
);
// reset value form field yang dihapus
setValue
(
`keterangan
${
newCount
+
1
}
`
,
null
);
}
};
//
const handleHapus = () => {
//
if (jumlahKeterangan > 0) {
//
const newCount = jumlahKeterangan - 1;
//
setJumlahKeterangan(newCount);
//
// reset value form field yang dihapus
//
setValue(`keterangan${newCount + 1}`, null);
//
}
//
};
const
MockNitku
=
[
{
...
...
src/sections/bupot-unifikasi/bupot-dokumen-dipersamakan/view/dokumen-dipersamakan-list-view.tsx
View file @
3fa64b2e
...
...
@@ -74,7 +74,7 @@ export function DokumenDipersamakanListView() {
.
join
(
'
AND
'
);
};
const
{
data
,
isLoading
,
isError
}
=
useGetDn
({
const
{
data
,
isLoading
}
=
useGetDn
({
params
:
{
Page
:
paginationModel
.
page
+
1
,
// API biasanya 1-based
Limit
:
paginationModel
.
pageSize
,
...
...
@@ -82,7 +82,9 @@ export function DokumenDipersamakanListView() {
sortingMode
:
sortModel
[
0
]?.
field
,
sortingMethod
:
sortModel
[
0
]?.
sort
,
},
option
:
{
refetchOnWindowFocus
:
false
,
},
});
const
totalRows
=
data
?.
total
||
0
;
...
...
src/sections/bupot-unifikasi/bupot-nr/view/nr-list-view.tsx
View file @
3fa64b2e
...
...
@@ -82,7 +82,9 @@ export function NrListView() {
sortingMode
:
sortModel
[
0
]?.
field
,
sortingMethod
:
sortModel
[
0
]?.
sort
,
},
option
:
{
refetchOnWindowFocus
:
false
,
},
});
const
totalRows
=
data
?.
total
||
0
;
...
...
@@ -141,10 +143,7 @@ export function NrListView() {
<
DashboardContent
>
<
CustomBreadcrumbs
heading=
"Bupot Non Residen"
links=
{
[
{
name
:
'
Dashboard
'
,
href
:
paths
.
dashboard
.
root
},
{
name
:
'
e-Bupot Non Residen
'
},
]
}
links=
{
[{
name
:
'
Dashboard
'
,
href
:
paths
.
dashboard
.
root
},
{
name
:
'
e-Bupot Non Residen
'
}]
}
action=
{
<
Button
component=
{
RouterLink
}
href=
{
paths
.
unifikasi
.
nrNew
}
variant=
"contained"
>
Rekam Data
...
...
src/sections/bupot-unifikasi/bupot-nr/view/nrRekamView.tsx
View file @
3fa64b2e
...
...
@@ -37,9 +37,9 @@ const NrRekamView = () => {
const
[
isOpenPanduan
,
setIsOpenPanduan
]
=
useState
<
boolean
>
(
false
);
const
[
isCheckedAgreement
,
setIsCheckedAgreement
]
=
useState
<
boolean
>
(
false
);
const
{
data
,
isLoading
,
isError
}
=
useGetKodeObjekPajak
();
const
{
data
}
=
useGetKodeObjekPajak
();
type
BpuFormData
=
z
.
infer
<
typeof
bpuSchema
>
;
//
type BpuFormData = z.infer<typeof bpuSchema>;
const
handleOpenPanduan
=
()
=>
setIsOpenPanduan
(
!
isOpenPanduan
);
...
...
src/sections/bupot-unifikasi/bupot-ssp/components/rekamSsp/Identitas.tsx
View file @
3fa64b2e
import
Grid
from
'
@mui/material/Grid
'
;
import
{
useState
}
from
'
react
'
;
import
{
useFormContext
}
from
'
react-hook-form
'
;
// import { useParams } from 'react-router';
import
{
Field
}
from
'
src/components/hook-form
'
;
...
...
@@ -10,30 +8,29 @@ type IdentitasProps = {
// disabledHapus: boolean;
};
const
Identitas
=
({
isPengganti
}:
IdentitasProps
)
=>
{
const
Identitas
=
({
isPengganti
}:
IdentitasProps
)
=>
(
// const { dnId } = useParams();
const
{
setValue
}
=
useFormContext
();
//
const { setValue } = useFormContext();
const
[
jumlahKeterangan
,
setJumlahKeterangan
]
=
useState
<
number
>
(
0
);
//
const [jumlahKeterangan, setJumlahKeterangan] = useState<number>(0);
const
maxKeterangan
=
5
;
//
const maxKeterangan = 5;
const
handleTambah
=
()
=>
{
if
(
jumlahKeterangan
<
maxKeterangan
)
{
setJumlahKeterangan
(
jumlahKeterangan
+
1
);
}
};
//
const handleTambah = () => {
//
if (jumlahKeterangan < maxKeterangan) {
//
setJumlahKeterangan(jumlahKeterangan + 1);
//
}
//
};
const
handleHapus
=
()
=>
{
if
(
jumlahKeterangan
>
0
)
{
const
newCount
=
jumlahKeterangan
-
1
;
setJumlahKeterangan
(
newCount
);
// reset value form field yang dihapus
setValue
(
`keterangan
${
newCount
+
1
}
`
,
null
);
}
};
//
const handleHapus = () => {
//
if (jumlahKeterangan > 0) {
//
const newCount = jumlahKeterangan - 1;
//
setJumlahKeterangan(newCount);
//
// reset value form field yang dihapus
//
setValue(`keterangan${newCount + 1}`, null);
//
}
//
};
return
(
<
Grid
container
rowSpacing=
{
2
}
alignItems=
"center"
columnSpacing=
{
2
}
>
<
Grid
size=
{
{
md
:
6
}
}
>
<
Field
.
DatePicker
name=
"tglPemotongan"
label=
"Tanggal Pemotongan"
/>
...
...
@@ -45,7 +42,5 @@ const Identitas = ({ isPengganti }: IdentitasProps) => {
<
Field
.
DatePicker
name=
"msPajak"
label=
"Masa Pajak"
view=
"month"
format=
"MM"
/>
</
Grid
>
</
Grid
>
);
};
);
export
default
Identitas
;
src/theme/with-settings/update-core.ts
View file @
3fa64b2e
...
...
@@ -36,10 +36,14 @@ export function applySettingsToTheme(
// const secondaryColorPalette = createPaletteChannel(secondaryColorPresets[primaryColor]);
const
updateColorScheme
=
(
schemeName
:
ThemeColorScheme
)
=>
{
const
currentScheme
=
theme
.
colorSchemes
?.[
schemeName
];
const
currentScheme
=
theme
.
colorSchemes
?.[
schemeName
]
as
any
;
if
(
!
currentScheme
||
typeof
currentScheme
!==
'
object
'
)
{
return
currentScheme
;
}
const
updatedPalette
=
{
...
currentScheme
?
.
palette
,
...
currentScheme
.
palette
,
...(
!
isDefaultPrimaryColor
&&
{
primary
:
primaryColorPalette
,
// secondary: secondaryColorPalette,
...
...
@@ -56,7 +60,7 @@ export function applySettingsToTheme(
};
const
updatedCustomShadows
=
{
...
currentScheme
?
.
customShadows
,
...
currentScheme
.
customShadows
,
...(
!
isDefaultPrimaryColor
&&
{
primary
:
createShadowColor
(
primaryColorPalette
.
mainChannel
),
// secondary: createShadowColor(secondaryColorPalette.mainChannel),
...
...
tsconfig.json
View file @
3fa64b2e
...
...
@@ -23,7 +23,8 @@
"strictNullChecks"
:
true
,
/*
✅
Tambahan
penting:
biar
TypeScript
tahu
lokasi
file
deklarasi
custom
*/
"typeRoots"
:
[
"./node_modules/@types"
,
"./src/@types"
]
"typeRoots"
:
[
"./node_modules/@types"
,
"./src/@types"
],
"types"
:
[
"node"
,
"vite/client"
]
//
Remove
"yup"
from
here
},
"include"
:
[
"src"
],
"exclude"
:
[
"node_modules"
],
...
...
yarn.lock
View file @
3fa64b2e
...
...
@@ -9108,7 +9108,7 @@ yoga-layout@^3.2.1:
resolved "https://registry.yarnpkg.com/yoga-layout/-/yoga-layout-3.2.1.tgz#d2d1ba06f0e81c2eb650c3e5ad8b0b4adde1e843"
integrity sha512-0LPOt3AxKqMdFBZA3HBAt/t/8vIKq7VaQYbuA8WxCgung+p9TVyKRYdpvCb80HcdTN2NkbIKbhNwKUfm3tQywQ==
yup@*:
yup@*
, yup@^1.7.1
:
version "1.7.1"
resolved "https://registry.yarnpkg.com/yup/-/yup-1.7.1.tgz#4c47c6bb367df08d4bc597f8c4c4f5fc4277f6ab"
integrity sha512-GKHFX2nXul2/4Dtfxhozv701jLQHdf6J34YDh2cEkpqoo8le5Mg6/LrdseVLrFarmFygZTlfIhHx/QKfb/QWXw==
...
...
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