@greatapps/common 1.1.257 → 1.1.258
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/account/AccountModals.mjs.map +1 -1
- package/dist/components/account/ConfigurationsMyAccountModal.mjs +1 -1
- package/dist/components/account/ConfigurationsMyAccountModal.mjs.map +1 -1
- package/dist/components/handlers/SearchParamsHandler.mjs.map +1 -1
- package/dist/components/modals/ModalManager.mjs.map +1 -1
- package/dist/components/modals/Modals.mjs.map +1 -1
- package/dist/components/widgets/notifications/NotificationCard.mjs.map +1 -1
- package/dist/hooks/useDebounce.mjs.map +1 -1
- package/dist/hooks/useDebounceState.mjs.map +1 -1
- package/dist/hooks/useDebouncedEffect.mjs.map +1 -1
- package/dist/infra/utils/clsx.mjs.map +1 -1
- package/dist/infra/utils/parser.mjs.map +1 -1
- package/dist/infra/utils/percentage.mjs.map +1 -1
- package/dist/modules/users/services/messages.service.mjs.map +1 -1
- package/dist/store/useAccountModals.mjs.map +1 -1
- package/dist/store/useModalManager.mjs.map +1 -1
- package/package.json +1 -1
- package/src/components/account/AccountModals.tsx +17 -17
- package/src/components/account/ConfigurationsMyAccountModal.tsx +1 -1
- package/src/components/handlers/SearchParamsHandler.tsx +61 -61
- package/src/components/modals/ModalManager.tsx +29 -29
- package/src/components/modals/Modals.tsx +26 -26
- package/src/components/widgets/notifications/NotificationCard.tsx +68 -68
- package/src/hooks/useDebounce.ts +26 -26
- package/src/hooks/useDebounceState.ts +11 -11
- package/src/hooks/useDebouncedEffect.ts +69 -69
- package/src/infra/utils/clsx.ts +6 -6
- package/src/infra/utils/parser.ts +52 -52
- package/src/infra/utils/percentage.ts +42 -42
- package/src/modules/users/services/messages.service.ts +48 -48
- package/src/store/useAccountModals.ts +55 -55
- package/src/store/useModalManager.ts +104 -104
|
@@ -1,68 +1,68 @@
|
|
|
1
|
-
import { IconClock } from '@tabler/icons-react';
|
|
2
|
-
import { ReactNode } from 'react';
|
|
3
|
-
import { cn } from '../../../infra/utils/clsx';
|
|
4
|
-
|
|
5
|
-
type NotificationCardProps = {
|
|
6
|
-
icon: ReactNode;
|
|
7
|
-
iconBgColor?: string;
|
|
8
|
-
title: string;
|
|
9
|
-
description?: string;
|
|
10
|
-
time: string;
|
|
11
|
-
actions?: ReactNode;
|
|
12
|
-
isUnread?: boolean;
|
|
13
|
-
showBorder?: boolean;
|
|
14
|
-
className?: string;
|
|
15
|
-
gapPx: number;
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
export default function NotificationCard({
|
|
19
|
-
icon,
|
|
20
|
-
iconBgColor,
|
|
21
|
-
title,
|
|
22
|
-
description,
|
|
23
|
-
time,
|
|
24
|
-
actions,
|
|
25
|
-
isUnread = false,
|
|
26
|
-
showBorder = true,
|
|
27
|
-
className,
|
|
28
|
-
gapPx = 16
|
|
29
|
-
}: NotificationCardProps) {
|
|
30
|
-
return (
|
|
31
|
-
<div
|
|
32
|
-
className={cn(
|
|
33
|
-
'flex flex-col lg:flex-row items-start p-4 lg:p-5 gap-3 lg:gap-4 relative',
|
|
34
|
-
showBorder && 'border-b border-gray-200',
|
|
35
|
-
className
|
|
36
|
-
)}
|
|
37
|
-
>
|
|
38
|
-
<div
|
|
39
|
-
className={cn(
|
|
40
|
-
'flex items-center justify-center size-10 rounded-full shrink-0',
|
|
41
|
-
iconBgColor
|
|
42
|
-
)}
|
|
43
|
-
>
|
|
44
|
-
{icon}
|
|
45
|
-
</div>
|
|
46
|
-
|
|
47
|
-
<div className="flex flex-col flex-1" style={{ gap: `${gapPx}px` }}>
|
|
48
|
-
<div className="flex flex-col gap-1">
|
|
49
|
-
<span className="paragraph-small-semibold text-gray-950">{title}</span>
|
|
50
|
-
{description && (
|
|
51
|
-
<span className="paragraph-small-regular text-gray-600">{description}</span>
|
|
52
|
-
)}
|
|
53
|
-
</div>
|
|
54
|
-
|
|
55
|
-
<div className="flex items-center gap-1.5">
|
|
56
|
-
<IconClock size={14} className="text-gray-400" />
|
|
57
|
-
<span className="paragraph-xsmall-medium text-gray-600">{time}</span>
|
|
58
|
-
</div>
|
|
59
|
-
|
|
60
|
-
{actions && <div className="flex items-center gap-2">{actions}</div>}
|
|
61
|
-
</div>
|
|
62
|
-
|
|
63
|
-
{isUnread && <div className="size-2 bg-pages-400 rounded-full shrink-0 mt-1" />}
|
|
64
|
-
</div>
|
|
65
|
-
);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
export type { NotificationCardProps };
|
|
1
|
+
import { IconClock } from '@tabler/icons-react';
|
|
2
|
+
import { ReactNode } from 'react';
|
|
3
|
+
import { cn } from '../../../infra/utils/clsx';
|
|
4
|
+
|
|
5
|
+
type NotificationCardProps = {
|
|
6
|
+
icon: ReactNode;
|
|
7
|
+
iconBgColor?: string;
|
|
8
|
+
title: string;
|
|
9
|
+
description?: string;
|
|
10
|
+
time: string;
|
|
11
|
+
actions?: ReactNode;
|
|
12
|
+
isUnread?: boolean;
|
|
13
|
+
showBorder?: boolean;
|
|
14
|
+
className?: string;
|
|
15
|
+
gapPx: number;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export default function NotificationCard({
|
|
19
|
+
icon,
|
|
20
|
+
iconBgColor,
|
|
21
|
+
title,
|
|
22
|
+
description,
|
|
23
|
+
time,
|
|
24
|
+
actions,
|
|
25
|
+
isUnread = false,
|
|
26
|
+
showBorder = true,
|
|
27
|
+
className,
|
|
28
|
+
gapPx = 16
|
|
29
|
+
}: NotificationCardProps) {
|
|
30
|
+
return (
|
|
31
|
+
<div
|
|
32
|
+
className={cn(
|
|
33
|
+
'flex flex-col lg:flex-row items-start p-4 lg:p-5 gap-3 lg:gap-4 relative',
|
|
34
|
+
showBorder && 'border-b border-gray-200',
|
|
35
|
+
className
|
|
36
|
+
)}
|
|
37
|
+
>
|
|
38
|
+
<div
|
|
39
|
+
className={cn(
|
|
40
|
+
'flex items-center justify-center size-10 rounded-full shrink-0',
|
|
41
|
+
iconBgColor
|
|
42
|
+
)}
|
|
43
|
+
>
|
|
44
|
+
{icon}
|
|
45
|
+
</div>
|
|
46
|
+
|
|
47
|
+
<div className="flex flex-col flex-1" style={{ gap: `${gapPx}px` }}>
|
|
48
|
+
<div className="flex flex-col gap-1">
|
|
49
|
+
<span className="paragraph-small-semibold text-gray-950">{title}</span>
|
|
50
|
+
{description && (
|
|
51
|
+
<span className="paragraph-small-regular text-gray-600">{description}</span>
|
|
52
|
+
)}
|
|
53
|
+
</div>
|
|
54
|
+
|
|
55
|
+
<div className="flex items-center gap-1.5">
|
|
56
|
+
<IconClock size={14} className="text-gray-400" />
|
|
57
|
+
<span className="paragraph-xsmall-medium text-gray-600">{time}</span>
|
|
58
|
+
</div>
|
|
59
|
+
|
|
60
|
+
{actions && <div className="flex items-center gap-2">{actions}</div>}
|
|
61
|
+
</div>
|
|
62
|
+
|
|
63
|
+
{isUnread && <div className="size-2 bg-pages-400 rounded-full shrink-0 mt-1" />}
|
|
64
|
+
</div>
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export type { NotificationCardProps };
|
package/src/hooks/useDebounce.ts
CHANGED
|
@@ -1,26 +1,26 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
import { useEffect, useState } from 'react';
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Hook para aplicar debounce em valores
|
|
7
|
-
* @param value - Valor a ser debounced
|
|
8
|
-
* @param delay - Delay em milissegundos (padrão: 400ms)
|
|
9
|
-
* @returns Valor debounced
|
|
10
|
-
*/
|
|
11
|
-
export function useDebounce<T>(value: T, delay: number = 400, onDebounce?: () => void): T {
|
|
12
|
-
const [debouncedValue, setDebouncedValue] = useState<T>(value);
|
|
13
|
-
|
|
14
|
-
useEffect(() => {
|
|
15
|
-
const handler = setTimeout(() => {
|
|
16
|
-
setDebouncedValue(value);
|
|
17
|
-
onDebounce?.();
|
|
18
|
-
}, delay);
|
|
19
|
-
|
|
20
|
-
return () => {
|
|
21
|
-
clearTimeout(handler);
|
|
22
|
-
};
|
|
23
|
-
}, [value, delay, onDebounce]);
|
|
24
|
-
|
|
25
|
-
return debouncedValue;
|
|
26
|
-
}
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useEffect, useState } from 'react';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Hook para aplicar debounce em valores
|
|
7
|
+
* @param value - Valor a ser debounced
|
|
8
|
+
* @param delay - Delay em milissegundos (padrão: 400ms)
|
|
9
|
+
* @returns Valor debounced
|
|
10
|
+
*/
|
|
11
|
+
export function useDebounce<T>(value: T, delay: number = 400, onDebounce?: () => void): T {
|
|
12
|
+
const [debouncedValue, setDebouncedValue] = useState<T>(value);
|
|
13
|
+
|
|
14
|
+
useEffect(() => {
|
|
15
|
+
const handler = setTimeout(() => {
|
|
16
|
+
setDebouncedValue(value);
|
|
17
|
+
onDebounce?.();
|
|
18
|
+
}, delay);
|
|
19
|
+
|
|
20
|
+
return () => {
|
|
21
|
+
clearTimeout(handler);
|
|
22
|
+
};
|
|
23
|
+
}, [value, delay, onDebounce]);
|
|
24
|
+
|
|
25
|
+
return debouncedValue;
|
|
26
|
+
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
import { useState } from 'react';
|
|
4
|
-
import type React from 'react';
|
|
5
|
-
import { useDebounce } from './useDebounce';
|
|
6
|
-
|
|
7
|
-
export function useDebounceState<T>(initialValue: T, delay: number = 400): [T, React.Dispatch<React.SetStateAction<T>>] {
|
|
8
|
-
const [rawValue, setRawValue] = useState<T>(initialValue);
|
|
9
|
-
const debouncedValue = useDebounce(rawValue, delay);
|
|
10
|
-
return [debouncedValue, setRawValue];
|
|
11
|
-
}
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useState } from 'react';
|
|
4
|
+
import type React from 'react';
|
|
5
|
+
import { useDebounce } from './useDebounce';
|
|
6
|
+
|
|
7
|
+
export function useDebounceState<T>(initialValue: T, delay: number = 400): [T, React.Dispatch<React.SetStateAction<T>>] {
|
|
8
|
+
const [rawValue, setRawValue] = useState<T>(initialValue);
|
|
9
|
+
const debouncedValue = useDebounce(rawValue, delay);
|
|
10
|
+
return [debouncedValue, setRawValue];
|
|
11
|
+
}
|
|
@@ -1,69 +1,69 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
import { useEffect, useRef, useState, useCallback } from 'react';
|
|
4
|
-
|
|
5
|
-
interface UseDebouncedEffectOptions {
|
|
6
|
-
effect: () => void;
|
|
7
|
-
debouncedDeps: React.DependencyList;
|
|
8
|
-
immediateDeps?: React.DependencyList;
|
|
9
|
-
delay?: number;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Executes an effect with two trigger modes:
|
|
14
|
-
* - `debouncedDeps`: triggers the effect after a delay (resets on each change)
|
|
15
|
-
* - `immediateDeps`: triggers the effect immediately (also cancels any pending debounce)
|
|
16
|
-
*
|
|
17
|
-
* On mount, the effect fires once immediately via `immediateDeps`.
|
|
18
|
-
* The effect callback always uses the latest closure values via a ref.
|
|
19
|
-
*
|
|
20
|
-
* @returns `{ isDebouncing }` — true while waiting for a debounced trigger to fire
|
|
21
|
-
*/
|
|
22
|
-
export function useDebouncedEffect({
|
|
23
|
-
effect,
|
|
24
|
-
debouncedDeps,
|
|
25
|
-
immediateDeps = [],
|
|
26
|
-
delay = 500,
|
|
27
|
-
}: UseDebouncedEffectOptions) {
|
|
28
|
-
const [isDebouncing, setIsDebouncing] = useState(false);
|
|
29
|
-
const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
30
|
-
const effectRef = useRef(effect);
|
|
31
|
-
effectRef.current = effect;
|
|
32
|
-
const hasMounted = useRef(false);
|
|
33
|
-
|
|
34
|
-
const clearTimer = useCallback(() => {
|
|
35
|
-
if (timerRef.current) {
|
|
36
|
-
clearTimeout(timerRef.current);
|
|
37
|
-
timerRef.current = null;
|
|
38
|
-
}
|
|
39
|
-
}, []);
|
|
40
|
-
|
|
41
|
-
useEffect(() => {
|
|
42
|
-
clearTimer();
|
|
43
|
-
setIsDebouncing(false);
|
|
44
|
-
effectRef.current();
|
|
45
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
46
|
-
}, immediateDeps);
|
|
47
|
-
|
|
48
|
-
useEffect(() => {
|
|
49
|
-
if (!hasMounted.current) {
|
|
50
|
-
hasMounted.current = true;
|
|
51
|
-
return;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
clearTimer();
|
|
55
|
-
setIsDebouncing(true);
|
|
56
|
-
|
|
57
|
-
timerRef.current = setTimeout(() => {
|
|
58
|
-
setIsDebouncing(false);
|
|
59
|
-
effectRef.current();
|
|
60
|
-
}, delay);
|
|
61
|
-
|
|
62
|
-
return clearTimer;
|
|
63
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
64
|
-
}, [...debouncedDeps, delay]);
|
|
65
|
-
|
|
66
|
-
useEffect(() => clearTimer, [clearTimer]);
|
|
67
|
-
|
|
68
|
-
return { isDebouncing };
|
|
69
|
-
}
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useEffect, useRef, useState, useCallback } from 'react';
|
|
4
|
+
|
|
5
|
+
interface UseDebouncedEffectOptions {
|
|
6
|
+
effect: () => void;
|
|
7
|
+
debouncedDeps: React.DependencyList;
|
|
8
|
+
immediateDeps?: React.DependencyList;
|
|
9
|
+
delay?: number;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Executes an effect with two trigger modes:
|
|
14
|
+
* - `debouncedDeps`: triggers the effect after a delay (resets on each change)
|
|
15
|
+
* - `immediateDeps`: triggers the effect immediately (also cancels any pending debounce)
|
|
16
|
+
*
|
|
17
|
+
* On mount, the effect fires once immediately via `immediateDeps`.
|
|
18
|
+
* The effect callback always uses the latest closure values via a ref.
|
|
19
|
+
*
|
|
20
|
+
* @returns `{ isDebouncing }` — true while waiting for a debounced trigger to fire
|
|
21
|
+
*/
|
|
22
|
+
export function useDebouncedEffect({
|
|
23
|
+
effect,
|
|
24
|
+
debouncedDeps,
|
|
25
|
+
immediateDeps = [],
|
|
26
|
+
delay = 500,
|
|
27
|
+
}: UseDebouncedEffectOptions) {
|
|
28
|
+
const [isDebouncing, setIsDebouncing] = useState(false);
|
|
29
|
+
const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
30
|
+
const effectRef = useRef(effect);
|
|
31
|
+
effectRef.current = effect;
|
|
32
|
+
const hasMounted = useRef(false);
|
|
33
|
+
|
|
34
|
+
const clearTimer = useCallback(() => {
|
|
35
|
+
if (timerRef.current) {
|
|
36
|
+
clearTimeout(timerRef.current);
|
|
37
|
+
timerRef.current = null;
|
|
38
|
+
}
|
|
39
|
+
}, []);
|
|
40
|
+
|
|
41
|
+
useEffect(() => {
|
|
42
|
+
clearTimer();
|
|
43
|
+
setIsDebouncing(false);
|
|
44
|
+
effectRef.current();
|
|
45
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
46
|
+
}, immediateDeps);
|
|
47
|
+
|
|
48
|
+
useEffect(() => {
|
|
49
|
+
if (!hasMounted.current) {
|
|
50
|
+
hasMounted.current = true;
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
clearTimer();
|
|
55
|
+
setIsDebouncing(true);
|
|
56
|
+
|
|
57
|
+
timerRef.current = setTimeout(() => {
|
|
58
|
+
setIsDebouncing(false);
|
|
59
|
+
effectRef.current();
|
|
60
|
+
}, delay);
|
|
61
|
+
|
|
62
|
+
return clearTimer;
|
|
63
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
64
|
+
}, [...debouncedDeps, delay]);
|
|
65
|
+
|
|
66
|
+
useEffect(() => clearTimer, [clearTimer]);
|
|
67
|
+
|
|
68
|
+
return { isDebouncing };
|
|
69
|
+
}
|
package/src/infra/utils/clsx.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { clsx, type ClassValue } from "clsx"
|
|
2
|
-
import { twMerge } from "tailwind-merge"
|
|
3
|
-
|
|
4
|
-
export function cn(...inputs: ClassValue[]) {
|
|
5
|
-
return twMerge(clsx(inputs))
|
|
6
|
-
}
|
|
1
|
+
import { clsx, type ClassValue } from "clsx"
|
|
2
|
+
import { twMerge } from "tailwind-merge"
|
|
3
|
+
|
|
4
|
+
export function cn(...inputs: ClassValue[]) {
|
|
5
|
+
return twMerge(clsx(inputs))
|
|
6
|
+
}
|
|
@@ -1,53 +1,53 @@
|
|
|
1
|
-
import z, { ZodTypeAny } from "zod";
|
|
2
|
-
import { PaginatedSuccessResult, SuccessResult } from "../api/types";
|
|
3
|
-
|
|
4
|
-
/* Overloads */
|
|
5
|
-
export function parseSchema<T extends ZodTypeAny>(
|
|
6
|
-
schema: T,
|
|
7
|
-
data: unknown[]
|
|
8
|
-
): z.infer<T>[];
|
|
9
|
-
|
|
10
|
-
export function parseSchema<T extends ZodTypeAny>(
|
|
11
|
-
schema: T,
|
|
12
|
-
data: unknown
|
|
13
|
-
): z.infer<T>;
|
|
14
|
-
|
|
15
|
-
export function parseSchema<T extends ZodTypeAny>(
|
|
16
|
-
schema: T,
|
|
17
|
-
data: unknown | unknown[]
|
|
18
|
-
) {
|
|
19
|
-
if (Array.isArray(data)) {
|
|
20
|
-
return data.map(item => schema.parse(item));
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
return schema.parse(data);
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export function parseResult<T extends ZodTypeAny>(
|
|
27
|
-
schema: T,
|
|
28
|
-
result: PaginatedSuccessResult<unknown>
|
|
29
|
-
): PaginatedSuccessResult<z.infer<T>>;
|
|
30
|
-
|
|
31
|
-
export function parseResult<T extends ZodTypeAny>(
|
|
32
|
-
schema: T,
|
|
33
|
-
result: SuccessResult<unknown>
|
|
34
|
-
): SuccessResult<z.infer<T>>;
|
|
35
|
-
|
|
36
|
-
export function parseResult<T extends ZodTypeAny>(
|
|
37
|
-
schema: T,
|
|
38
|
-
result: SuccessResult<unknown> | PaginatedSuccessResult<unknown>
|
|
39
|
-
) {
|
|
40
|
-
if (!result.data) return result;
|
|
41
|
-
|
|
42
|
-
if (Array.isArray(result.data)) {
|
|
43
|
-
return {
|
|
44
|
-
...result,
|
|
45
|
-
data: result.data.map(item => schema.parse(item)),
|
|
46
|
-
};
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
return {
|
|
50
|
-
...result,
|
|
51
|
-
data: schema.parse(result.data),
|
|
52
|
-
};
|
|
1
|
+
import z, { ZodTypeAny } from "zod";
|
|
2
|
+
import { PaginatedSuccessResult, SuccessResult } from "../api/types";
|
|
3
|
+
|
|
4
|
+
/* Overloads */
|
|
5
|
+
export function parseSchema<T extends ZodTypeAny>(
|
|
6
|
+
schema: T,
|
|
7
|
+
data: unknown[]
|
|
8
|
+
): z.infer<T>[];
|
|
9
|
+
|
|
10
|
+
export function parseSchema<T extends ZodTypeAny>(
|
|
11
|
+
schema: T,
|
|
12
|
+
data: unknown
|
|
13
|
+
): z.infer<T>;
|
|
14
|
+
|
|
15
|
+
export function parseSchema<T extends ZodTypeAny>(
|
|
16
|
+
schema: T,
|
|
17
|
+
data: unknown | unknown[]
|
|
18
|
+
) {
|
|
19
|
+
if (Array.isArray(data)) {
|
|
20
|
+
return data.map(item => schema.parse(item));
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return schema.parse(data);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function parseResult<T extends ZodTypeAny>(
|
|
27
|
+
schema: T,
|
|
28
|
+
result: PaginatedSuccessResult<unknown>
|
|
29
|
+
): PaginatedSuccessResult<z.infer<T>>;
|
|
30
|
+
|
|
31
|
+
export function parseResult<T extends ZodTypeAny>(
|
|
32
|
+
schema: T,
|
|
33
|
+
result: SuccessResult<unknown>
|
|
34
|
+
): SuccessResult<z.infer<T>>;
|
|
35
|
+
|
|
36
|
+
export function parseResult<T extends ZodTypeAny>(
|
|
37
|
+
schema: T,
|
|
38
|
+
result: SuccessResult<unknown> | PaginatedSuccessResult<unknown>
|
|
39
|
+
) {
|
|
40
|
+
if (!result.data) return result;
|
|
41
|
+
|
|
42
|
+
if (Array.isArray(result.data)) {
|
|
43
|
+
return {
|
|
44
|
+
...result,
|
|
45
|
+
data: result.data.map(item => schema.parse(item)),
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return {
|
|
50
|
+
...result,
|
|
51
|
+
data: schema.parse(result.data),
|
|
52
|
+
};
|
|
53
53
|
}
|
|
@@ -1,42 +1,42 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Calcula percentual de um valor em relação ao total
|
|
3
|
-
* @param current - Valor atual
|
|
4
|
-
* @param total - Valor total
|
|
5
|
-
* @returns Percentual (0-100)
|
|
6
|
-
*/
|
|
7
|
-
export const calculatePercentage = (current: number, total: number): number => {
|
|
8
|
-
if (total === 0) return 0;
|
|
9
|
-
return (current / total) * 100;
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Calcula a circunferência de um círculo
|
|
14
|
-
* @param radius - Raio do círculo
|
|
15
|
-
* @returns Circunferência
|
|
16
|
-
*/
|
|
17
|
-
export const calculateCircumference = (radius: number): number => {
|
|
18
|
-
return 2 * Math.PI * radius;
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Calcula o offset para circular progress
|
|
23
|
-
* @param percentage - Percentual de progresso (0-100)
|
|
24
|
-
* @param circumference - Circunferência do círculo
|
|
25
|
-
* @returns Valor do offset
|
|
26
|
-
*/
|
|
27
|
-
export const calculateCircularProgressOffset = (
|
|
28
|
-
percentage: number,
|
|
29
|
-
circumference: number
|
|
30
|
-
): number => {
|
|
31
|
-
return circumference - (percentage / 100) * circumference;
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Calcula raio de círculo baseado no tamanho e stroke
|
|
36
|
-
* @param size - Tamanho total
|
|
37
|
-
* @param strokeWidth - Largura do stroke
|
|
38
|
-
* @returns Raio calculado
|
|
39
|
-
*/
|
|
40
|
-
export const calculateRadius = (size: number, strokeWidth: number): number => {
|
|
41
|
-
return (size - strokeWidth) / 2;
|
|
42
|
-
};
|
|
1
|
+
/**
|
|
2
|
+
* Calcula percentual de um valor em relação ao total
|
|
3
|
+
* @param current - Valor atual
|
|
4
|
+
* @param total - Valor total
|
|
5
|
+
* @returns Percentual (0-100)
|
|
6
|
+
*/
|
|
7
|
+
export const calculatePercentage = (current: number, total: number): number => {
|
|
8
|
+
if (total === 0) return 0;
|
|
9
|
+
return (current / total) * 100;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Calcula a circunferência de um círculo
|
|
14
|
+
* @param radius - Raio do círculo
|
|
15
|
+
* @returns Circunferência
|
|
16
|
+
*/
|
|
17
|
+
export const calculateCircumference = (radius: number): number => {
|
|
18
|
+
return 2 * Math.PI * radius;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Calcula o offset para circular progress
|
|
23
|
+
* @param percentage - Percentual de progresso (0-100)
|
|
24
|
+
* @param circumference - Circunferência do círculo
|
|
25
|
+
* @returns Valor do offset
|
|
26
|
+
*/
|
|
27
|
+
export const calculateCircularProgressOffset = (
|
|
28
|
+
percentage: number,
|
|
29
|
+
circumference: number
|
|
30
|
+
): number => {
|
|
31
|
+
return circumference - (percentage / 100) * circumference;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Calcula raio de círculo baseado no tamanho e stroke
|
|
36
|
+
* @param size - Tamanho total
|
|
37
|
+
* @param strokeWidth - Largura do stroke
|
|
38
|
+
* @returns Raio calculado
|
|
39
|
+
*/
|
|
40
|
+
export const calculateRadius = (size: number, strokeWidth: number): number => {
|
|
41
|
+
return (size - strokeWidth) / 2;
|
|
42
|
+
};
|
|
@@ -1,48 +1,48 @@
|
|
|
1
|
-
import 'server-only';
|
|
2
|
-
|
|
3
|
-
import { api } from '../../../infra/api/client';
|
|
4
|
-
import { buildQueryParams } from '../../../infra/utils/params';
|
|
5
|
-
import { getUserContext } from '../../auth/utils/get-user-context';
|
|
6
|
-
import {
|
|
7
|
-
FindMessagesParams,
|
|
8
|
-
ListMessagesApiResponse,
|
|
9
|
-
MarkAsReadApiResponse,
|
|
10
|
-
MarkAllAsReadApiResponse,
|
|
11
|
-
} from '../schema/messages.schema';
|
|
12
|
-
|
|
13
|
-
class MessagesService {
|
|
14
|
-
async listMessages(
|
|
15
|
-
params?: Partial<FindMessagesParams>
|
|
16
|
-
): Promise<ListMessagesApiResponse> {
|
|
17
|
-
const { id_account, id_user } = await getUserContext();
|
|
18
|
-
|
|
19
|
-
const query = buildQueryParams({
|
|
20
|
-
id_account,
|
|
21
|
-
id_user,
|
|
22
|
-
type: 'notification',
|
|
23
|
-
limit: 20,
|
|
24
|
-
page: 1,
|
|
25
|
-
order: 'desc',
|
|
26
|
-
...params,
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
const url = `/accounts/${id_account}/messages${query ? `?${query}` : ''}`;
|
|
30
|
-
return api.apps.get<ListMessagesApiResponse>(url);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
async markAsRead(messageId: string): Promise<MarkAsReadApiResponse> {
|
|
34
|
-
const { id_account } = await getUserContext();
|
|
35
|
-
|
|
36
|
-
const url = `/accounts/${id_account}/messages/${messageId}/action/markAsRead`;
|
|
37
|
-
return api.apps.put<MarkAsReadApiResponse>(url);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
async markAllAsRead(): Promise<MarkAllAsReadApiResponse> {
|
|
41
|
-
const { id_account, id_user } = await getUserContext();
|
|
42
|
-
|
|
43
|
-
const url = `/accounts/${id_account}/messages/action/markAllAsRead`;
|
|
44
|
-
return api.apps.put<MarkAllAsReadApiResponse>(url, { id_user });
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
export const messagesService = new MessagesService();
|
|
1
|
+
import 'server-only';
|
|
2
|
+
|
|
3
|
+
import { api } from '../../../infra/api/client';
|
|
4
|
+
import { buildQueryParams } from '../../../infra/utils/params';
|
|
5
|
+
import { getUserContext } from '../../auth/utils/get-user-context';
|
|
6
|
+
import {
|
|
7
|
+
FindMessagesParams,
|
|
8
|
+
ListMessagesApiResponse,
|
|
9
|
+
MarkAsReadApiResponse,
|
|
10
|
+
MarkAllAsReadApiResponse,
|
|
11
|
+
} from '../schema/messages.schema';
|
|
12
|
+
|
|
13
|
+
class MessagesService {
|
|
14
|
+
async listMessages(
|
|
15
|
+
params?: Partial<FindMessagesParams>
|
|
16
|
+
): Promise<ListMessagesApiResponse> {
|
|
17
|
+
const { id_account, id_user } = await getUserContext();
|
|
18
|
+
|
|
19
|
+
const query = buildQueryParams({
|
|
20
|
+
id_account,
|
|
21
|
+
id_user,
|
|
22
|
+
type: 'notification',
|
|
23
|
+
limit: 20,
|
|
24
|
+
page: 1,
|
|
25
|
+
order: 'desc',
|
|
26
|
+
...params,
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
const url = `/accounts/${id_account}/messages${query ? `?${query}` : ''}`;
|
|
30
|
+
return api.apps.get<ListMessagesApiResponse>(url);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async markAsRead(messageId: string): Promise<MarkAsReadApiResponse> {
|
|
34
|
+
const { id_account } = await getUserContext();
|
|
35
|
+
|
|
36
|
+
const url = `/accounts/${id_account}/messages/${messageId}/action/markAsRead`;
|
|
37
|
+
return api.apps.put<MarkAsReadApiResponse>(url);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async markAllAsRead(): Promise<MarkAllAsReadApiResponse> {
|
|
41
|
+
const { id_account, id_user } = await getUserContext();
|
|
42
|
+
|
|
43
|
+
const url = `/accounts/${id_account}/messages/action/markAllAsRead`;
|
|
44
|
+
return api.apps.put<MarkAllAsReadApiResponse>(url, { id_user });
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export const messagesService = new MessagesService();
|