Skip to content

Commit 56ef258

Browse files
committed
feat: csv collapse threshold option
1 parent bc24680 commit 56ef258

6 files changed

Lines changed: 165 additions & 2 deletions

File tree

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
import React, { ReactElement, useState } from 'react'
2+
import style from './account.module.css'
3+
import { DEFAULT_CSV_COLLAPSE_THRESHOLD } from 'constants/index'
4+
5+
interface IProps {
6+
csvCollapseThreshold: number
7+
}
8+
9+
export default function ChangeCsvCollapseThreshold ({ csvCollapseThreshold }: IProps): ReactElement {
10+
const [threshold, setThreshold] = useState(csvCollapseThreshold ?? DEFAULT_CSV_COLLAPSE_THRESHOLD)
11+
const [inputValue, setInputValue] = useState(String(csvCollapseThreshold ?? DEFAULT_CSV_COLLAPSE_THRESHOLD))
12+
const [error, setError] = useState('')
13+
const [success, setSuccess] = useState('')
14+
const [disabled, setDisabled] = useState(true)
15+
16+
const onSubmit = async (e: React.FormEvent): Promise<void> => {
17+
e.preventDefault()
18+
const newThreshold = parseInt(inputValue, 10)
19+
if (isNaN(newThreshold) || newThreshold < 0) {
20+
setError('Please enter a valid non-negative number')
21+
return
22+
}
23+
24+
const oldThreshold = threshold
25+
setThreshold(newThreshold)
26+
setDisabled(true)
27+
28+
try {
29+
const res = await fetch('/api/user/csvCollapseThreshold', {
30+
method: 'PUT',
31+
headers: {
32+
'Content-Type': 'application/json'
33+
},
34+
body: JSON.stringify({ csvCollapseThreshold: newThreshold })
35+
})
36+
if (res.status === 200) {
37+
setError('')
38+
setSuccess('Updated successfully.')
39+
setTimeout(() => {
40+
setSuccess('')
41+
}, 3000)
42+
} else {
43+
throw new Error('Failed to update threshold')
44+
}
45+
} catch (err: any) {
46+
setSuccess('')
47+
setError(err.message ?? 'Failed to update threshold')
48+
setThreshold(oldThreshold)
49+
setInputValue(String(oldThreshold))
50+
}
51+
}
52+
53+
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>): void => {
54+
const value = e.target.value
55+
setInputValue(value)
56+
const numValue = parseInt(value, 10)
57+
if (!isNaN(numValue) && numValue >= 0 && numValue !== threshold) {
58+
setDisabled(false)
59+
setError('')
60+
} else if (numValue === threshold) {
61+
setDisabled(true)
62+
} else {
63+
setDisabled(true)
64+
if (value !== '') {
65+
setError('Please enter a valid non-negative number')
66+
}
67+
}
68+
}
69+
70+
return (
71+
<div className={style.threshold_ctn}>
72+
<form onSubmit={(e) => { void onSubmit(e) }}>
73+
<div className={style.threshold_row}>
74+
<input
75+
id="csvCollapseThreshold"
76+
type="text"
77+
min="0"
78+
required
79+
value={inputValue}
80+
onChange={handleInputChange}
81+
className={style.threshold_input}
82+
/>
83+
<button disabled={disabled} className='button_main' type='submit'>Update</button>
84+
</div>
85+
{error !== '' && <div className={style.error_message}>{error}</div>}
86+
{success !== '' && <div className={style.success_message}>{success}</div>}
87+
</form>
88+
</div>
89+
)
90+
}

components/Account/account.module.css

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,3 +78,34 @@ body[data-theme="dark"] .pro_ctn input {
7878
margin: 0 0 10px 0;
7979
font-weight: 600;
8080
}
81+
82+
.threshold_ctn {
83+
width: 100%;
84+
margin-top: 10px;
85+
}
86+
87+
.threshold_row {
88+
display: flex;
89+
gap: 10px;
90+
align-items: center;
91+
}
92+
93+
.threshold_input {
94+
width: 80px;
95+
padding: 5px 10px;
96+
border: 1px solid #ccc;
97+
border-radius: 5px;
98+
font-size: 14px;
99+
}
100+
101+
body[data-theme='dark'] .threshold_input {
102+
border-color: #a5a5a5;
103+
background-color: var(--primary-bg-color);
104+
color: var(--primary-text-color);
105+
}
106+
107+
.threshold_ctn button {
108+
padding: 5px 15px;
109+
border-radius: 5px;
110+
font-size: 14px;
111+
}

pages/account/index.tsx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { GetServerSideProps } from 'next'
77
import Page from 'components/Page'
88
import ChangePassword from 'components/Account/ChangePassword'
99
import ChangeFiatCurrency from 'components/Account/ChangeFiatCurrency'
10+
import ChangeCsvCollapseThreshold from 'components/Account/ChangeCsvCollapseThreshold'
1011
import style from './account.module.css'
1112
import { fetchUserProfileFromId, fetchUserWithSupertokens, getUserPublicKeyHex, UserWithSupertokens } from 'services/userService'
1213
import CopyIcon from '../../assets/copy-black.png'
@@ -147,6 +148,15 @@ export default function Account ({ user, userPublicKey, organization, orgMembers
147148
/>
148149
</div>
149150

151+
<div className={style.account_row}>
152+
<div className={style.label}>CSV Collapse Threshold</div>
153+
<div className={style.value}>
154+
<ChangeCsvCollapseThreshold
155+
csvCollapseThreshold={userProfile.csvCollapseThreshold}
156+
/>
157+
</div>
158+
</div>
159+
150160
<div className={style.account_row}>
151161
<div className={style.label}>
152162
Public Key{' '}

pages/api/paybutton/download/transactions/[paybuttonId].ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ export default async (req: any, res: any): Promise<void> => {
5252
};
5353
const transactions = await fetchTransactionsByPaybuttonId(paybutton.id, networkIdArray)
5454
res.setHeader('Content-Type', 'text/csv')
55-
await downloadTxsFile(res, quoteSlug, timezone, transactions, userId, paybuttonId)
55+
await downloadTxsFile(res, quoteSlug, timezone, transactions, userId, paybuttonId, true, user.csvCollapseThreshold)
5656
} catch (error: any) {
5757
switch (error.message) {
5858
case RESPONSE_MESSAGES.PAYBUTTON_ID_NOT_PROVIDED_400.message:

pages/api/payments/download/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ export default async (req: any, res: any): Promise<void> => {
6262

6363
const transactions = await fetchAllPaymentsByUserId(userId, networkIdArray, buttonIds, years, startDate, endDate, timezone)
6464

65-
await downloadTxsFile(res, quoteSlug, timezone, transactions, userId)
65+
await downloadTxsFile(res, quoteSlug, timezone, transactions, userId, undefined, true, user.csvCollapseThreshold)
6666
} catch (error: any) {
6767
switch (error.message) {
6868
case RESPONSE_MESSAGES.METHOD_NOT_ALLOWED_405.message:
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { setSession } from 'utils/setSession'
2+
import * as userService from 'services/userService'
3+
import { RESPONSE_MESSAGES } from 'constants/index'
4+
5+
export default async (
6+
req: any,
7+
res: any
8+
): Promise<void> => {
9+
await setSession(req, res, true)
10+
11+
if (req.method === 'PUT') {
12+
const session = req.session
13+
const { csvCollapseThreshold } = req.body
14+
15+
if (csvCollapseThreshold === undefined || csvCollapseThreshold === null) {
16+
res.status(400).json({ message: 'csvCollapseThreshold is required' })
17+
return
18+
}
19+
20+
const threshold = Number(csvCollapseThreshold)
21+
if (isNaN(threshold) || threshold < 0) {
22+
res.status(400).json({ message: 'csvCollapseThreshold must be a non-negative number' })
23+
return
24+
}
25+
26+
await userService.updateCsvCollapseThreshold(session.userId, threshold)
27+
res.status(200).json({ success: true })
28+
} else {
29+
res.status(RESPONSE_MESSAGES.METHOD_NOT_ALLOWED_405.statusCode)
30+
.json(RESPONSE_MESSAGES.METHOD_NOT_ALLOWED_405)
31+
}
32+
}

0 commit comments

Comments
 (0)