Skip to content

Commit 438102b

Browse files
committed
chore: Update Chip component styles and add category select in TransactionDetails
1 parent 5786200 commit 438102b

9 files changed

Lines changed: 203 additions & 18 deletions

File tree

client/src/components/Chip/index.module.scss

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,19 @@
22
border-radius: 0.5rem;
33
padding: 0.5rem 1rem;
44
width: fit-content;
5+
display: flex;
6+
align-items: center;
7+
gap: 0.5rem;
8+
9+
&:hover button {
10+
color: #42b983;
11+
background-color: rgba(66, 185, 131, 0.1);
12+
}
13+
}
14+
15+
.clickable {
16+
cursor: pointer;
17+
padding-right: 0.5rem;
518
}
619

720
.default {

client/src/components/Chip/index.tsx

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,22 @@ import classes from "./index.module.scss";
33
import classNames from "classnames";
44

55
type Props = {
6-
label: string | number;
6+
label: string | number | JSX.Element;
77
type?: "default" | "success" | "error" | "warning" | "info";
8+
onClick?: () => void;
89
};
910

10-
const Chip: FC<Props> = ({ label, type = "default" }) => {
11+
const Chip: FC<Props> = ({ label, type = "default", onClick }) => {
1112
return (
12-
<div className={classNames(classes["root"], classes[type])}>{label}</div>
13+
<div
14+
className={classNames(classes["root"], classes[type], {
15+
[classes["clickable"]]: !!onClick,
16+
})}
17+
onClick={onClick}
18+
tabIndex={onClick ? 0 : undefined}
19+
>
20+
{label}
21+
</div>
1322
);
1423
};
1524

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
.root {
2+
position: relative;
3+
box-sizing: border-box;
4+
width: fit-content;
5+
6+
&::after {
7+
content: "";
8+
font-size: 1rem;
9+
top: 10px;
10+
right: 10px;
11+
position: absolute;
12+
transition: color 0.15s ease-in-out;
13+
pointer-events: none;
14+
}
15+
16+
&:hover {
17+
&::after {
18+
color: #42b983;
19+
}
20+
.select {
21+
border-color: #42b983;
22+
}
23+
}
24+
}
25+
26+
.select {
27+
appearance: none;
28+
padding: 0.5rem;
29+
padding-right: 30px;
30+
border: 2px solid #ccc;
31+
border-radius: 0.25rem;
32+
font-size: 1rem;
33+
color: #333;
34+
background-color: #fff;
35+
cursor: pointer;
36+
transition: border-color 0.15s ease-in-out;
37+
38+
&:focus {
39+
outline: none;
40+
border-color: #42b983;
41+
}
42+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import { DetailedHTMLProps, FC } from "react";
2+
import { UseFormRegisterReturn } from "react-hook-form";
3+
import classes from "./index.module.scss";
4+
5+
type InputProps = DetailedHTMLProps<
6+
React.SelectHTMLAttributes<HTMLSelectElement>,
7+
HTMLSelectElement
8+
>;
9+
10+
type Props = {
11+
label?: string;
12+
options: string[];
13+
isRequired?: boolean;
14+
defaultValue?: string;
15+
errorMessage?: string;
16+
inputProps?: InputProps;
17+
formProps?: UseFormRegisterReturn;
18+
showEmptyOption?: boolean;
19+
};
20+
21+
// will be use with react-hook-form
22+
const Select: FC<Props> = ({
23+
label,
24+
options,
25+
isRequired,
26+
defaultValue,
27+
errorMessage,
28+
inputProps,
29+
formProps,
30+
showEmptyOption,
31+
}) => {
32+
return (
33+
<div className={classes["root"]}>
34+
{label && (
35+
<label htmlFor={inputProps?.id}>
36+
{label}
37+
{isRequired && <span className="text-red-500"> *</span>}
38+
</label>
39+
)}
40+
<select
41+
{...inputProps}
42+
{...formProps}
43+
defaultValue={defaultValue}
44+
className={classes["select"]}
45+
>
46+
{showEmptyOption && <option value="" />}
47+
{options.map((option) => (
48+
<option key={option} value={option}>
49+
{option}
50+
</option>
51+
))}
52+
</select>
53+
{errorMessage && (
54+
<p className="mt-2 text-sm text-red-600">{errorMessage}</p>
55+
)}
56+
</div>
57+
);
58+
};
59+
60+
export default Select;

client/src/pages/profile/account-page/TransactionDetails.module.scss

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
display: flex;
1010
gap: 0.5rem;
1111
align-items: center;
12-
min-height: 36px;
12+
min-height: 50px;
1313

1414
.label {
1515
font-weight: 600;

client/src/pages/profile/account-page/TransactionDetails.tsx

Lines changed: 48 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import IconButton from "../../../components/IconButton";
77
import Chip from "../../../components/Chip";
88
import { useAppDispatch } from "../../../redux/hooks";
99
import { updateTransaction } from "../../../redux/transactions/transactionsActions";
10+
import Select from "../../../components/Select";
11+
import { TransactionCategory } from "./constants";
1012

1113
type ItemProps = {
1214
transactionId: string;
@@ -25,7 +27,7 @@ const Item: FC<ItemProps> = ({
2527
useChip,
2628
chipColor,
2729
}) => {
28-
const { register, handleSubmit, watch } = useForm();
30+
const { register, handleSubmit, watch, setValue } = useForm();
2931
const [isEditing, setIsEditing] = useState(false);
3032

3133
const dispatch = useAppDispatch();
@@ -34,6 +36,11 @@ const Item: FC<ItemProps> = ({
3436
setIsEditing(true);
3537
};
3638

39+
const handleCancel = () => {
40+
setIsEditing(false);
41+
setValue("value", value);
42+
};
43+
3744
const onSubmit = () => {
3845
setIsEditing(false);
3946

@@ -72,28 +79,57 @@ const Item: FC<ItemProps> = ({
7279
className={classes["value-container"]}
7380
onSubmit={handleSubmit(onSubmit)}
7481
>
75-
<Input
76-
type="text"
77-
formProps={register("value")}
78-
inputProps={{ defaultValue: value }}
79-
isDirty={!!watch("value")}
80-
/>
82+
{label === "Category" ? (
83+
<Select
84+
options={Object.values(TransactionCategory)}
85+
formProps={register("value")}
86+
inputProps={{ defaultValue: value }}
87+
/>
88+
) : (
89+
<Input
90+
type="text"
91+
formProps={register("value")}
92+
inputProps={{ defaultValue: value }}
93+
isDirty={!!watch("value")}
94+
/>
95+
)}
8196
<IconButton
8297
icon={<i className="fa fa-check" aria-hidden="true"></i>}
8398
submit
8499
/>
100+
<IconButton
101+
icon={<i className="fa fa-times" aria-hidden="true"></i>}
102+
onClick={handleCancel}
103+
/>
85104
</form>
86105
) : (
87106
<div className={classes["value-container"]}>
88107
{useChip ? (
89-
<Chip label={value} type={chipColor} />
108+
<Chip
109+
onClick={handleEdit}
110+
label={
111+
label === "Category" ? (
112+
<>
113+
{value}
114+
<IconButton
115+
icon={<i className="fa fa-pencil" aria-hidden="true"></i>}
116+
/>
117+
</>
118+
) : (
119+
value
120+
)
121+
}
122+
type={chipColor}
123+
/>
90124
) : (
91125
<span className={classes["value"]}>{value}</span>
92126
)}
93-
<IconButton
94-
icon={<i className="fa fa-pencil" aria-hidden="true"></i>}
95-
onClick={handleEdit}
96-
/>
127+
{label !== "Category" && (
128+
<IconButton
129+
icon={<i className="fa fa-pencil" aria-hidden="true"></i>}
130+
onClick={handleEdit}
131+
/>
132+
)}
97133
</div>
98134
)}
99135
</div>
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
export enum TransactionCategory {
2+
NoCategory = "No category",
3+
Entertainment = "Entertainment",
4+
Food = "Food",
5+
Transport = "Transport",
6+
Other = "Other",
7+
Shopping = "Shopping",
8+
Utilities = "Utilities",
9+
Rent = "Rent",
10+
Healthcare = "Healthcare",
11+
Education = "Education",
12+
Travel = "Travel",
13+
}

client/src/redux/transactions/transactionsActions.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ export const updateTransaction = createAsyncThunk(
7979
data: { body },
8080
} = await axios.put(Endpoints.transactions.BASE({ transactionId: id }), {
8181
notes,
82-
category,
82+
category: category,
8383
});
8484

8585
dispatch(updateTransactionState(body as Transaction));

client/src/types/transactions.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,23 @@
1+
export type TransactionCategory =
2+
| "No category"
3+
| "Entertainment"
4+
| "Food"
5+
| "Transport"
6+
| "Other"
7+
| "Shopping"
8+
| "Utilities"
9+
| "Rent"
10+
| "Healthcare"
11+
| "Education"
12+
| "Travel";
113
export type TransactionStatus = "PENDING" | "COMPLETED" | "REJECTED" | "ERROR";
214

315
export type Transaction = {
416
accountId: string;
517
accountName: string;
618
amount: number;
719
balanceLeft: number;
8-
category: string;
20+
category: TransactionCategory;
921
createdAt: Date | string;
1022
description: string;
1123
id: string;

0 commit comments

Comments
 (0)