Skip to content
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { generateApiKey, getKeyPrefix, hashApiKey } from "../../middlewares/apiK
* Create a new API key pair (public + secret) for a partner
* POST /v1/admin/partners/:partnerName/api-keys
*/
export async function createApiKey(req: Request, res: Response): Promise<void> {
export async function createApiKey(req: Request<{ partnerName: string }>, res: Response): Promise<void> {
try {
const partnerName = req.params.partnerName as string;
const { name, expiresAt } = req.body;
Expand Down Expand Up @@ -110,7 +110,7 @@ export async function createApiKey(req: Request, res: Response): Promise<void> {
* List all API keys for a partner (by name)
* GET /v1/admin/partners/:partnerName/api-keys
*/
export async function listApiKeys(req: Request, res: Response): Promise<void> {
export async function listApiKeys(req: Request<{ partnerName: string }>, res: Response): Promise<void> {
try {
const partnerName = req.params.partnerName as string;

Expand Down Expand Up @@ -180,7 +180,7 @@ export async function listApiKeys(req: Request, res: Response): Promise<void> {
* Revoke (soft delete) an API key
* DELETE /v1/admin/partners/:partnerName/api-keys/:keyId
*/
export async function revokeApiKey(req: Request, res: Response): Promise<void> {
export async function revokeApiKey(req: Request<{ partnerName: string; keyId: string }>, res: Response): Promise<void> {
try {
const { partnerName, keyId } = req.params;

Expand Down
2 changes: 1 addition & 1 deletion apps/api/src/api/controllers/maintenance.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ export const getAllMaintenanceSchedules: RequestHandler = async (_, res) => {
* @returns {Object} 404 - Schedule not found
* @returns {Object} 500 - Internal server error
*/
export const updateScheduleActiveStatus: RequestHandler = async (req, res) => {
export const updateScheduleActiveStatus: RequestHandler<{ id: string }> = async (req, res) => {
try {
const id = req.params.id as string;
const { isActive } = req.body;
Expand Down
2 changes: 1 addition & 1 deletion apps/frontend/.storybook/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { dirname, join } from "path";
* This function is used to resolve the absolute path of a package.
* It is needed in projects that use Yarn PnP or are set up within a monorepo.
*/
function getAbsolutePath(value: string): any {
function getAbsolutePath(value: string) {
return dirname(require.resolve(join(value, "package.json")));
}
const config: StorybookConfig = {
Expand Down
78 changes: 39 additions & 39 deletions apps/frontend/src/components/Accordion/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { AnimatePresence, motion } from "motion/react";
import { AnimatePresence, motion, useReducedMotion } from "motion/react";
import { FC } from "react";
import { create } from "zustand";
import { durations, easings } from "../../constants/animations";
import { cn } from "../../helpers/cn";

interface AccordionProps {
Expand Down Expand Up @@ -44,6 +45,7 @@ const useAccordionStore = create<AccordionStore>(set => ({

const Accordion: FC<AccordionProps> = ({ children, className = "", defaultValue = [] }) => {
const setValue = useAccordionStore(state => state.setValue);
const shouldReduceMotion = useReducedMotion();

if (defaultValue.length > 0) {
setValue(defaultValue);
Expand All @@ -53,8 +55,8 @@ const Accordion: FC<AccordionProps> = ({ children, className = "", defaultValue
<motion.div
animate={{ opacity: 1, y: 0 }}
className={cn("mx-auto w-full max-w-3xl", className)}
initial={{ opacity: 0, y: 20 }}
transition={{ duration: 0.4 }}
initial={shouldReduceMotion ? false : { opacity: 0, y: 20 }}
transition={shouldReduceMotion ? { duration: 0 } : { duration: durations.slow, ease: easings.easeOutCubic }}
>
{children}
</motion.div>
Expand All @@ -63,83 +65,81 @@ const Accordion: FC<AccordionProps> = ({ children, className = "", defaultValue

const AccordionItem: FC<AccordionItemProps> = ({ children, className = "", value }) => {
const isOpen = useAccordionStore(state => state.value.includes(value));
const shouldReduceMotion = useReducedMotion();

return (
<motion.div
animate={{ opacity: 1 }}
className={cn("border-gray-200 border-b last:border-b-0", className)}
initial={{ opacity: 0 }}
transition={{ duration: 0.3 }}
initial={shouldReduceMotion ? false : { opacity: 0 }}
transition={shouldReduceMotion ? { duration: 0 } : { duration: durations.slow }}
>
<motion.div
className="bg-white transition-colors duration-200 hover:bg-gray-50"
data-state={isOpen ? "open" : "closed"}
layout
>
<div className="bg-white transition-colors duration-200 hover:bg-gray-50" data-state={isOpen ? "open" : "closed"}>
{children}
</motion.div>
</div>
</motion.div>
);
};

const AccordionTrigger: FC<AccordionTriggerProps> = ({ children, className = "", value }) => {
const toggleValue = useAccordionStore(state => state.toggleValue);
const isOpen = useAccordionStore(state => state.value.includes(value));
const shouldReduceMotion = useReducedMotion();

return (
<motion.div className="flex" layout>
<div className="flex">
<motion.button
className={cn(
"w-full cursor-pointer px-6 py-4 text-left font-medium text-base text-gray-900 transition-all duration-200 hover:text-blue-700 focus:outline-none md:text-lg",
"w-full cursor-pointer px-6 py-4 text-left font-medium text-base text-gray-900 transition-colors duration-200 hover:text-blue-700 focus:outline-none md:text-lg",
className
)}
onClick={() => toggleValue(value)}
whileHover={{ scale: 1.01 }}
whileTap={{ scale: 0.99 }}
whileHover={shouldReduceMotion ? undefined : { scale: 1.01 }}
whileTap={shouldReduceMotion ? undefined : { scale: 0.99 }}
>
<div className="flex items-center justify-between">
<motion.span layout>{children}</motion.span>
<span>{children}</span>
<motion.svg
animate={{ rotate: isOpen ? 180 : 0 }}
className="h-5 w-5 text-blue-700"
fill="none"
stroke="currentColor"
transition={{ duration: 0.3, ease: "easeInOut" }}
transition={shouldReduceMotion ? { duration: 0 } : { duration: durations.slow, ease: easings.easeOutCubic }}
viewBox="0 0 24 24"
>
<path d="M19 9l-7 7-7-7" strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} />
</motion.svg>
</div>
</motion.button>
</motion.div>
</div>
);
};

const AccordionContent: FC<AccordionContentProps> = ({ children, className = "", value }) => {
const isOpen = useAccordionStore(state => state.value.includes(value));
const shouldReduceMotion = useReducedMotion();

return (
<AnimatePresence initial={false}>
{isOpen && (
<motion.div
animate={{ height: "auto", opacity: 1 }}
className="overflow-hidden"
exit={{ height: 0, opacity: 0 }}
initial={{ height: 0, opacity: 0 }}
transition={{ duration: 0.3, ease: "easeInOut" }}
>
<motion.div
animate={{ y: 0 }}
className={cn("px-6 pb-6 text-gray-600 leading-relaxed", className)}
exit={{ y: -10 }}
initial={{ y: -10 }}
transition={{ duration: 0.3 }}
>
{children}
</motion.div>
</motion.div>
)}
</AnimatePresence>
<div
className="grid transition-[grid-template-rows] duration-300 ease-out motion-reduce:transition-none"
style={{ gridTemplateRows: isOpen ? "1fr" : "0fr" }}
>
<div className="overflow-hidden">
<AnimatePresence initial={false}>
{isOpen && (
<motion.div
animate={{ opacity: 1, y: 0 }}
className={cn("px-6 pb-6 text-gray-600 leading-relaxed", className)}
exit={shouldReduceMotion ? { opacity: 0 } : { opacity: 0, y: -10 }}
initial={shouldReduceMotion ? { opacity: 0 } : { opacity: 0, y: -10 }}
transition={shouldReduceMotion ? { duration: 0 } : { duration: durations.slow, ease: easings.easeOutCubic }}
>
{children}
</motion.div>
)}
</AnimatePresence>
</div>
</div>
);
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,11 @@ export const AveniaKYBVerifyStep = ({
<div>
<h1 className="mt-2 mb-4 text-center font-bold text-2xl text-blue-700">{t(titleKey)}</h1>

<img alt="Business Check" className="mx-auto mt-16 w-[170px] transition-all duration-300" src={imageSrc} />
<img
alt="Business Check"
className="mx-auto mt-16 w-[170px] transition-opacity duration-300 motion-reduce:transition-none"
src={imageSrc}
/>

{!isVerificationStarted && (
<p className="mx-1 mt-6 mb-4 text-center">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,11 @@
import { CNPJ_REGEX, CPF_REGEX, isValidCnpj, isValidCpf, RampDirection } from "@vortexfi/shared";
import { AnimatePresence, type MotionProps, motion } from "motion/react";
import { AnimatePresence, motion, useReducedMotion } from "motion/react";
import type { FC } from "react";
import { Trans, useTranslation } from "react-i18next";
import { durations, easings } from "../../../constants/animations";
import { useRampDirection } from "../../../stores/rampDirectionStore";
import { AveniaField, AveniaFieldValidationPattern, StandardAveniaFieldOptions } from "../AveniaField";

const containerAnimation: MotionProps = {
animate: { height: "auto", opacity: 1 },
exit: { height: 0, opacity: 0 },
initial: { height: 0, opacity: 0 },
transition: { duration: 0.3 }
};

const OFFRAMP_FIELDS = [
{ id: StandardAveniaFieldOptions.TAX_ID, index: 0, label: "cpfOrCnpj" },
{ id: StandardAveniaFieldOptions.PIX_ID, index: 1, label: "pixKey" }
Expand Down Expand Up @@ -46,12 +40,18 @@ export const AveniaKycEligibilityFields: FC<{ isWalletAddressDisabled?: boolean
const { t } = useTranslation();
const rampDirection = useRampDirection();
const isOnramp = rampDirection === RampDirection.BUY;
const shouldReduceMotion = useReducedMotion();

const FIELDS = isOnramp ? ONRAMP_FIELDS : OFFRAMP_FIELDS;

return (
<AnimatePresence>
<motion.div {...containerAnimation}>
<motion.div
animate={{ opacity: 1, y: 0 }}
exit={shouldReduceMotion ? { opacity: 0 } : { opacity: 0, y: -10 }}
initial={shouldReduceMotion ? { opacity: 0 } : { opacity: 0, y: -10 }}
transition={shouldReduceMotion ? { duration: 0 } : { duration: durations.slow, ease: easings.easeOutCubic }}
>
{FIELDS.map(field => (
<AveniaField
className="mt-2"
Expand Down
47 changes: 24 additions & 23 deletions apps/frontend/src/components/CollapsibleCard/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { AnimatePresence, motion } from "motion/react";
import { AnimatePresence, motion, useReducedMotion } from "motion/react";
import { createContext, forwardRef, ReactNode, useContext, useId, useState } from "react";
import { durations, easings } from "../../constants/animations";

interface CollapsibleCardProps {
children: ReactNode;
Expand Down Expand Up @@ -66,30 +67,30 @@ const CollapsibleSummary = ({ children, className = "" }: CollapsibleSummaryProp

const CollapsibleDetails = ({ children, className = "" }: CollapsibleDetailsProps) => {
const { isExpanded, detailsId } = useCollapsibleCard();
const shouldReduceMotion = useReducedMotion();

return (
<AnimatePresence>
{isExpanded && (
<motion.div
animate={{ height: "auto", opacity: 1 }}
className={`overflow-hidden ${className}`}
exit={{ height: 0, opacity: 0 }}
initial={{ height: 0, opacity: 0 }}
transition={{ duration: 0.3, ease: "easeInOut" }}
>
<motion.div
animate={{ y: 0 }}
className="mt-4 border-gray-200 border-t pt-4"
exit={{ y: -10 }}
id={detailsId}
initial={{ y: -10 }}
transition={{ duration: 0.3 }}
>
{children}
</motion.div>
</motion.div>
)}
</AnimatePresence>
<div
className={`grid transition-[grid-template-rows] duration-300 ease-out motion-reduce:transition-none ${className}`}
style={{ gridTemplateRows: isExpanded ? "1fr" : "0fr" }}
>
<div className="overflow-hidden">
<AnimatePresence>
{isExpanded && (
<motion.div
animate={{ opacity: 1, y: 0 }}
className="mt-4 border-gray-200 border-t pt-4"
exit={shouldReduceMotion ? { opacity: 0 } : { opacity: 0, y: -10 }}
id={detailsId}
initial={shouldReduceMotion ? { opacity: 0 } : { opacity: 0, y: -10 }}
transition={shouldReduceMotion ? { duration: 0 } : { duration: durations.slow, ease: easings.easeOutCubic }}
>
{children}
</motion.div>
)}
</AnimatePresence>
</div>
</div>
);
};

Expand Down
2 changes: 1 addition & 1 deletion apps/frontend/src/components/EmailForm/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ export const EmailForm = ({ transactionId, transactionSuccess }: EmailFormProps)
</div>
{!isPending && !isSuccess && (
<button
className="h-12! min-w-24 cursor-pointer rounded bg-blue-600 px-5 py-2 text-center font-medium text-white transition-all duration-200 hover:bg-blue-700 active:scale-95"
className="h-12! min-w-24 cursor-pointer rounded bg-blue-600 px-5 py-2 text-center font-medium text-white transition-[background-color,transform] duration-200 ease-out hover:bg-blue-700 active:scale-95 motion-reduce:transition-none"
disabled={isPending}
type="submit"
>
Expand Down
24 changes: 16 additions & 8 deletions apps/frontend/src/components/KycLevel2Toggle/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { AveniaDocumentType } from "@vortexfi/shared";
import { motion } from "motion/react";
import { motion, useReducedMotion } from "motion/react";

interface KycLevel2ToggleProps {
activeDocType: AveniaDocumentType;
Expand All @@ -8,16 +8,20 @@ interface KycLevel2ToggleProps {
}

export const KycLevel2Toggle = ({ activeDocType, onToggle }: KycLevel2ToggleProps) => {
const shouldReduceMotion = useReducedMotion();

return (
<div className="relative mb-6 flex justify-center">
<button
className={`relative z-10 w-full flex-1 px-4 py-2 text-center font-bold text-2xl transition-all duration-300 ${"text-gray-500"}`}
className={`relative z-10 w-full flex-1 px-4 py-2 text-center font-bold text-2xl transition-colors duration-300 ${
activeDocType === AveniaDocumentType.ID ? "text-blue-700" : "text-gray-500 hover:text-gray-700"
}`}
onClick={() => onToggle(AveniaDocumentType.ID)}
>
RG
</button>
<button
className={`relative z-10 flex-1 px-4 py-2 text-center font-bold text-2xl transition-all duration-300 ${
className={`relative z-10 flex-1 px-4 py-2 text-center font-bold text-2xl transition-colors duration-300 ${
activeDocType === AveniaDocumentType.DRIVERS_LICENSE ? "text-blue-700" : "text-gray-500 hover:text-gray-700"
}`}
onClick={() => onToggle(AveniaDocumentType.DRIVERS_LICENSE)}
Expand All @@ -32,11 +36,15 @@ export const KycLevel2Toggle = ({ activeDocType, onToggle }: KycLevel2ToggleProp
left: activeDocType === AveniaDocumentType.ID ? "0%" : "50%",
width: "50%"
}}
transition={{
bounce: 0.2,
duration: 0.6,
type: "spring"
}}
transition={
shouldReduceMotion
? { duration: 0 }
: {
bounce: 0.2,
duration: 0.6,
type: "spring"
}
}
/>
</div>
);
Expand Down
Loading