This commit is contained in:
2026-03-02 15:42:25 +08:00
26 changed files with 1403 additions and 171 deletions

View File

@@ -0,0 +1,42 @@
"use client";
import { useState } from "react";
import CheckIcon from "../icons/check";
import styles from "./styles.module.css";
import CloseIcon from "../icons/close";
import CheckedIcon from "../icons/switchIcons/checked";
import UnCheckedIcon from "../icons/switchIcons/unchecked";
const CustomCheckbox = ({ checked, onChange, id }) => {
const [isChecked, setIsChecked] = useState(false);
return (
<>
<input
type="checkbox"
id={id}
className={styles.check}
checked={checked}
onChange={() => {
setIsChecked(!isChecked);
onChange;
}}
/>
<label htmlFor={id} className={styles.switch}>
<span className={styles.knob}>
{isChecked ? (
<CheckedIcon
width={16}
height={16}
color="#4F378A"
viewBox="0 0 20 20"
/>
) : (
<UnCheckedIcon width={16} height={16} color="#fff" />
)}
</span>
</label>
</>
);
};
export default CustomCheckbox;

View File

@@ -0,0 +1,46 @@
.switch {
display: flex;
width: 53px;
height: 33px;
align-items: center;
background-color: white;
cursor: pointer;
padding: 0 2px;
border-radius: 150px;
position: relative;
border: 2px solid #6d6d6d;
overflow: hidden;
}
.check {
display: none;
}
/* The knob (replaces ::before) */
.knob {
width: 25px;
height: 25px;
background-color: #6d6d6d;
border-radius: 50%;
display: flex;
justify-content: center;
align-items: center;
transition: all 0.15s ease-in-out;
}
.knob:hover {
box-shadow: 0 0 0px 5px #33333327;
background-color: #333;
}
.check:checked + .switch .knob {
transform: translateX(78%);
background-color: white;
}
/* Optional background change */
.check:checked + .switch {
background-color: #8187ff;
border: 2px solid #8187ff;
}

View File

@@ -25,6 +25,15 @@
border: 1px solid #959aff;
background: rgba(75, 79, 109, 0.25);
}
.input:active::placeholder,
.input:focus::placeholder {
color: #4b4f6d;
font-family: Inter;
font-size: 16px;
font-style: normal;
font-weight: 400;
line-height: normal;
}
.input:hover {
background: rgba(75, 79, 109, 0.25);
}

View File

@@ -0,0 +1,28 @@
const ArrowDownIcon = ({
width = 18,
height = 18,
color = "#4C4F6B",
...props
}) => {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
width={width}
height={height}
viewBox="0 0 18 18"
fill="none"
{...props}
>
<path
d="M1.55859 5.2793L9.00047 12.7212L16.4423 5.2793"
stroke={color}
strokeWidth={1.5}
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
);
};
export default ArrowDownIcon;

View File

@@ -0,0 +1,30 @@
import React from "react";
const CheckedIcon = ({
width = 20,
height = 20,
color = "white",
strokeWidth = 1.5,
...props
}) => {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
width={20}
height={20}
viewBox="0 0 28 28"
fill="none"
{...props}
>
<path
d="M4.53125 10L8.4375 13.9062L16.25 6.09375"
stroke={color}
strokeWidth={strokeWidth}
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
);
};
export default CheckedIcon;

View File

@@ -0,0 +1,39 @@
import React from "react";
const UnCheckedIcon = ({
width = 20,
height = 20,
color = "white",
strokeWidth = 1.5,
viewBox = "0 0 28 28",
...props
}) => {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
width={width}
height={height}
viewBox={viewBox}
fill="none"
{...props}
>
<path
d="M20.7077 7.29199L7.29102 20.7087"
stroke={color}
strokeWidth={strokeWidth}
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M7.29102 7.29199L20.7077 20.7087"
stroke={color}
strokeWidth={strokeWidth}
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
);
};
export default UnCheckedIcon;

View File

@@ -0,0 +1,38 @@
import React from "react";
const WarningIcon = (props) => {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
width={20}
height={20}
viewBox="0 0 20 20"
fill="none"
{...props}
>
<path
d="M2.5 10C2.5 10.9849 2.69399 11.9602 3.0709 12.8701C3.44781 13.7801 4.00026 14.6069 4.6967 15.3033C5.39314 15.9997 6.21993 16.5522 7.12987 16.9291C8.03982 17.306 9.01509 17.5 10 17.5C10.9849 17.5 11.9602 17.306 12.8701 16.9291C13.7801 16.5522 14.6069 15.9997 15.3033 15.3033C15.9997 14.6069 16.5522 13.7801 16.9291 12.8701C17.306 11.9602 17.5 10.9849 17.5 10C17.5 8.01088 16.7098 6.10322 15.3033 4.6967C13.8968 3.29018 11.9891 2.5 10 2.5C8.01088 2.5 6.10322 3.29018 4.6967 4.6967C3.29018 6.10322 2.5 8.01088 2.5 10Z"
stroke="#CCA04F"
strokeWidth={1.5}
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M10 6.66699V10.0003"
stroke="#CCA04F"
strokeWidth={1.5}
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M10 13.333H10.0083"
stroke="#CCA04F"
strokeWidth={1.5}
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
);
};
export default WarningIcon;

View File

@@ -0,0 +1,71 @@
"use client";
import React, { useState, useRef, useEffect } from "react";
import styles from "./styles.module.css";
import ArrowDownIcon from "../icons/arrowDown";
const SelectField = ({ label, options, ...props }) => {
const [showOptions, setShowOptions] = useState(false);
const [dropUp, setDropUp] = useState(false);
const selectRef = useRef(null);
const [currentSelected, setCurrentSelected] = useState({
label: label,
value: "",
});
useEffect(() => {
if (showOptions && selectRef.current) {
const rect = selectRef.current.getBoundingClientRect();
const spaceBelow = window.innerHeight - rect.bottom;
const spaceAbove = rect.top;
const dropdownHeight = 150;
if (spaceBelow < dropdownHeight && spaceAbove > dropdownHeight) {
setDropUp(true);
} else {
setDropUp(false);
}
}
}, [showOptions]);
return (
<div
ref={selectRef}
className={styles.select}
onClick={() => setShowOptions(!showOptions)}
{...props}
>
<label
className={`${styles.label} ${currentSelected.value ? styles.selected : ""}`}
>
{currentSelected.label}
</label>
<ArrowDownIcon />
{showOptions && (
<div
className={`${styles.optionsContainer} ${
dropUp ? styles.dropUp : ""
}`}
>
{options.map((opt, key) => {
return (
<div
key={key}
onClick={() => {
setCurrentSelected(opt);
setShowOptions(false);
}}
>
<p>{opt.label}</p>
</div>
);
})}
</div>
)}
</div>
);
};
export default SelectField;

View File

@@ -0,0 +1,83 @@
.select {
display: flex;
padding: 12px;
align-items: flex-start;
align-items: center;
justify-content: space-between;
gap: 10px;
align-self: stretch;
border-radius: 4px;
border: 1px solid #4b4f6d;
background-color: transparent;
position: relative;
}
.label {
color: #858699;
font-family: Inter;
font-size: 16px;
font-style: normal;
font-weight: 400;
line-height: normal;
}
.selected {
color: white;
}
.optionsContainer {
display: flex;
padding: 6px;
flex-direction: column;
align-items: flex-start;
align-self: stretch;
position: absolute;
width: 100%;
left: 0;
border-radius: 6px;
border: 1px solid #353a4c;
background: #282b39;
animation-name: showDD;
animation-duration: 200ms;
z-index: 20;
}
@keyframes showDD {
0% {
opacity: 0;
transform: translateY(-10%);
}
100% {
opacity: 1;
transform: translateY(0%);
}
}
.optionsContainer > div {
display: flex;
padding: 8px;
align-items: center;
gap: 10px;
align-self: stretch;
border-radius: 4px;
}
.optionsContainer p {
color: #acb0ff;
font-family: Inter;
font-size: 18px;
font-style: normal;
font-weight: 400;
line-height: normal;
}
.optionsContainer > div:hover,
.optionsContainer > div:hover p {
background: #3c4159;
color: white;
}
.optionsContainer {
top: 0%;
margin-top: 6px;
}
/* When flipping upward */
.dropUp {
bottom: 0%;
top: auto;
margin-bottom: 6px;
}

View File

@@ -65,7 +65,7 @@ const TopHeader = (props) => {
{props.state === "add" ? (
<>
<div className={styles.button}>
<CheckIcon />
<CheckIcon width={20} height={20} />
<p>{props.buttonText}</p>
</div>
<div className={styles.cancelButton} onClick={() => router.back()}>

View File

@@ -44,7 +44,7 @@
display: flex;
justify-content: flex-end;
align-items: center;
gap: 10px;
gap: 5px;
}
.searchBarContainer {
display: flex;
@@ -55,14 +55,14 @@
}
.button {
display: flex;
padding: 8px 12px;
padding: 7px 15px;
justify-content: center;
align-items: center;
gap: 8px;
gap: 10px;
border-radius: 6px;
border-radius: 6px;
border: 0.5px solid #8187ff;
background: rgba(83, 89, 242, 0.25);
cursor: pointer;
}
.button:hover {
background: linear-gradient(180deg, #5359f2 0%, #2e3288 100%);