@greatapps/common 1.0.0

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.
Files changed (205) hide show
  1. package/dist/components/handlers/SearchParamsHandler.mjs +23 -0
  2. package/dist/components/handlers/SearchParamsHandler.mjs.map +1 -0
  3. package/dist/components/layouts/MainLayout.mjs +60 -0
  4. package/dist/components/layouts/MainLayout.mjs.map +1 -0
  5. package/dist/components/layouts/MdSideBarNavigation.mjs +36 -0
  6. package/dist/components/layouts/MdSideBarNavigation.mjs.map +1 -0
  7. package/dist/components/layouts/NavBar.mjs +45 -0
  8. package/dist/components/layouts/NavBar.mjs.map +1 -0
  9. package/dist/components/layouts/NotificationItem.mjs +7 -0
  10. package/dist/components/layouts/NotificationItem.mjs.map +1 -0
  11. package/dist/components/layouts/SideBarNavigation.mjs +22 -0
  12. package/dist/components/layouts/SideBarNavigation.mjs.map +1 -0
  13. package/dist/components/ui/buttons/Button.mjs +51 -0
  14. package/dist/components/ui/buttons/Button.mjs.map +1 -0
  15. package/dist/components/ui/buttons/CancelButton.mjs +16 -0
  16. package/dist/components/ui/buttons/CancelButton.mjs.map +1 -0
  17. package/dist/components/ui/buttons/CopyButton.mjs +34 -0
  18. package/dist/components/ui/buttons/CopyButton.mjs.map +1 -0
  19. package/dist/components/ui/data-display/Accordion.mjs +73 -0
  20. package/dist/components/ui/data-display/Accordion.mjs.map +1 -0
  21. package/dist/components/ui/data-display/Badge.mjs +41 -0
  22. package/dist/components/ui/data-display/Badge.mjs.map +1 -0
  23. package/dist/components/ui/data-display/Card.mjs +91 -0
  24. package/dist/components/ui/data-display/Card.mjs.map +1 -0
  25. package/dist/components/ui/data-display/Pagination.mjs +115 -0
  26. package/dist/components/ui/data-display/Pagination.mjs.map +1 -0
  27. package/dist/components/ui/data-display/ScrollArea.mjs +60 -0
  28. package/dist/components/ui/data-display/ScrollArea.mjs.map +1 -0
  29. package/dist/components/ui/data-display/Separator.mjs +28 -0
  30. package/dist/components/ui/data-display/Separator.mjs.map +1 -0
  31. package/dist/components/ui/data-display/Table.mjs +116 -0
  32. package/dist/components/ui/data-display/Table.mjs.map +1 -0
  33. package/dist/components/ui/data-display/Tabs.mjs +69 -0
  34. package/dist/components/ui/data-display/Tabs.mjs.map +1 -0
  35. package/dist/components/ui/feedback/CircularProgress.mjs +61 -0
  36. package/dist/components/ui/feedback/CircularProgress.mjs.map +1 -0
  37. package/dist/components/ui/feedback/DefaultCircularProgress.mjs +49 -0
  38. package/dist/components/ui/feedback/DefaultCircularProgress.mjs.map +1 -0
  39. package/dist/components/ui/feedback/Progress.mjs +37 -0
  40. package/dist/components/ui/feedback/Progress.mjs.map +1 -0
  41. package/dist/components/ui/feedback/Toast.mjs +73 -0
  42. package/dist/components/ui/feedback/Toast.mjs.map +1 -0
  43. package/dist/components/ui/form/Calendar.mjs +203 -0
  44. package/dist/components/ui/form/Calendar.mjs.map +1 -0
  45. package/dist/components/ui/form/Checkbox.mjs +33 -0
  46. package/dist/components/ui/form/Checkbox.mjs.map +1 -0
  47. package/dist/components/ui/form/DatePicker.mjs +78 -0
  48. package/dist/components/ui/form/DatePicker.mjs.map +1 -0
  49. package/dist/components/ui/form/DateRangePicker.mjs +86 -0
  50. package/dist/components/ui/form/DateRangePicker.mjs.map +1 -0
  51. package/dist/components/ui/form/Input.mjs +20 -0
  52. package/dist/components/ui/form/Input.mjs.map +1 -0
  53. package/dist/components/ui/form/InputOtp.mjs +67 -0
  54. package/dist/components/ui/form/InputOtp.mjs.map +1 -0
  55. package/dist/components/ui/form/RadioGroup.mjs +36 -0
  56. package/dist/components/ui/form/RadioGroup.mjs.map +1 -0
  57. package/dist/components/ui/form/Select.mjs +172 -0
  58. package/dist/components/ui/form/Select.mjs.map +1 -0
  59. package/dist/components/ui/form/Switch.mjs +33 -0
  60. package/dist/components/ui/form/Switch.mjs.map +1 -0
  61. package/dist/components/ui/form/Textarea.mjs +19 -0
  62. package/dist/components/ui/form/Textarea.mjs.map +1 -0
  63. package/dist/components/ui/overlay/Command.mjs +170 -0
  64. package/dist/components/ui/overlay/Command.mjs.map +1 -0
  65. package/dist/components/ui/overlay/Dialog.mjs +131 -0
  66. package/dist/components/ui/overlay/Dialog.mjs.map +1 -0
  67. package/dist/components/ui/overlay/Popover.mjs +46 -0
  68. package/dist/components/ui/overlay/Popover.mjs.map +1 -0
  69. package/dist/components/ui/overlay/Sheet.mjs +129 -0
  70. package/dist/components/ui/overlay/Sheet.mjs.map +1 -0
  71. package/dist/components/ui/overlay/Tooltip.mjs +55 -0
  72. package/dist/components/ui/overlay/Tooltip.mjs.map +1 -0
  73. package/dist/hooks/copy-to-clipboard.hook.mjs +39 -0
  74. package/dist/hooks/copy-to-clipboard.hook.mjs.map +1 -0
  75. package/dist/index.mjs +57 -0
  76. package/dist/index.mjs.map +1 -0
  77. package/dist/infra/api/client.mjs +115 -0
  78. package/dist/infra/api/client.mjs.map +1 -0
  79. package/dist/infra/api/types.mjs +31 -0
  80. package/dist/infra/api/types.mjs.map +1 -0
  81. package/dist/infra/utils/client-info.mjs +27 -0
  82. package/dist/infra/utils/client-info.mjs.map +1 -0
  83. package/dist/infra/utils/clsx.mjs +9 -0
  84. package/dist/infra/utils/clsx.mjs.map +1 -0
  85. package/dist/infra/utils/params.mjs +21 -0
  86. package/dist/infra/utils/params.mjs.map +1 -0
  87. package/dist/infra/utils/percentage.mjs +20 -0
  88. package/dist/infra/utils/percentage.mjs.map +1 -0
  89. package/dist/middlewares/chain.mjs +37 -0
  90. package/dist/middlewares/chain.mjs.map +1 -0
  91. package/dist/middlewares/create-auth-middleware.mjs +28 -0
  92. package/dist/middlewares/create-auth-middleware.mjs.map +1 -0
  93. package/dist/middlewares/types.mjs +11 -0
  94. package/dist/middlewares/types.mjs.map +1 -0
  95. package/dist/middlewares/validate-session.mjs +92 -0
  96. package/dist/middlewares/validate-session.mjs.map +1 -0
  97. package/dist/modules/auth/actions/login.action.mjs +11 -0
  98. package/dist/modules/auth/actions/login.action.mjs.map +1 -0
  99. package/dist/modules/auth/actions/logout.action.mjs +11 -0
  100. package/dist/modules/auth/actions/logout.action.mjs.map +1 -0
  101. package/dist/modules/auth/actions/register.action.mjs +9 -0
  102. package/dist/modules/auth/actions/register.action.mjs.map +1 -0
  103. package/dist/modules/auth/actions/validate-session.action.mjs +44 -0
  104. package/dist/modules/auth/actions/validate-session.action.mjs.map +1 -0
  105. package/dist/modules/auth/hooks/login.hook.mjs +12 -0
  106. package/dist/modules/auth/hooks/login.hook.mjs.map +1 -0
  107. package/dist/modules/auth/hooks/logout.hook.mjs +12 -0
  108. package/dist/modules/auth/hooks/logout.hook.mjs.map +1 -0
  109. package/dist/modules/auth/hooks/register.hook.mjs +12 -0
  110. package/dist/modules/auth/hooks/register.hook.mjs.map +1 -0
  111. package/dist/modules/auth/hooks/useUserQuery.mjs +48 -0
  112. package/dist/modules/auth/hooks/useUserQuery.mjs.map +1 -0
  113. package/dist/modules/auth/schema.mjs +1 -0
  114. package/dist/modules/auth/schema.mjs.map +1 -0
  115. package/dist/modules/auth/services/auth.service.mjs +163 -0
  116. package/dist/modules/auth/services/auth.service.mjs.map +1 -0
  117. package/dist/modules/users/action/find-user-by-id.action.mjs +21 -0
  118. package/dist/modules/users/action/find-user-by-id.action.mjs.map +1 -0
  119. package/dist/modules/users/schema.mjs +11 -0
  120. package/dist/modules/users/schema.mjs.map +1 -0
  121. package/dist/modules/users/services/user.service.mjs +50 -0
  122. package/dist/modules/users/services/user.service.mjs.map +1 -0
  123. package/dist/modules/whitelabel/actions/find-whitelabel.action.mjs +19 -0
  124. package/dist/modules/whitelabel/actions/find-whitelabel.action.mjs.map +1 -0
  125. package/dist/modules/whitelabel/schema.mjs +1 -0
  126. package/dist/modules/whitelabel/schema.mjs.map +1 -0
  127. package/dist/modules/whitelabel/services/whitelabel.service.mjs +62 -0
  128. package/dist/modules/whitelabel/services/whitelabel.service.mjs.map +1 -0
  129. package/dist/providers/auth.provider.mjs +84 -0
  130. package/dist/providers/auth.provider.mjs.map +1 -0
  131. package/dist/providers/query.provider.mjs +37 -0
  132. package/dist/providers/query.provider.mjs.map +1 -0
  133. package/dist/providers/whitelabel.provider.mjs +33 -0
  134. package/dist/providers/whitelabel.provider.mjs.map +1 -0
  135. package/dist/store/useMdSidebarStore.mjs +11 -0
  136. package/dist/store/useMdSidebarStore.mjs.map +1 -0
  137. package/package.json +68 -0
  138. package/src/components/handlers/SearchParamsHandler.tsx +61 -0
  139. package/src/components/layouts/MainLayout.tsx +59 -0
  140. package/src/components/layouts/MdSideBarNavigation.tsx +39 -0
  141. package/src/components/layouts/NavBar.tsx +79 -0
  142. package/src/components/layouts/NotificationItem.tsx +26 -0
  143. package/src/components/layouts/SideBarNavigation.tsx +22 -0
  144. package/src/components/ui/buttons/Button.tsx +55 -0
  145. package/src/components/ui/buttons/CancelButton.tsx +12 -0
  146. package/src/components/ui/buttons/CopyButton.tsx +47 -0
  147. package/src/components/ui/data-display/Accordion.tsx +79 -0
  148. package/src/components/ui/data-display/Badge.tsx +45 -0
  149. package/src/components/ui/data-display/Card.tsx +91 -0
  150. package/src/components/ui/data-display/Pagination.tsx +124 -0
  151. package/src/components/ui/data-display/ScrollArea.tsx +57 -0
  152. package/src/components/ui/data-display/Separator.tsx +27 -0
  153. package/src/components/ui/data-display/Table.tsx +115 -0
  154. package/src/components/ui/data-display/Tabs.tsx +65 -0
  155. package/src/components/ui/feedback/CircularProgress.tsx +75 -0
  156. package/src/components/ui/feedback/DefaultCircularProgress.tsx +53 -0
  157. package/src/components/ui/feedback/Progress.tsx +38 -0
  158. package/src/components/ui/feedback/Toast.tsx +99 -0
  159. package/src/components/ui/form/Calendar.tsx +226 -0
  160. package/src/components/ui/form/Checkbox.tsx +31 -0
  161. package/src/components/ui/form/DatePicker.tsx +97 -0
  162. package/src/components/ui/form/DateRangePicker.tsx +108 -0
  163. package/src/components/ui/form/Input.tsx +18 -0
  164. package/src/components/ui/form/InputOtp.tsx +78 -0
  165. package/src/components/ui/form/RadioGroup.tsx +43 -0
  166. package/src/components/ui/form/Select.tsx +195 -0
  167. package/src/components/ui/form/Switch.tsx +30 -0
  168. package/src/components/ui/form/Textarea.tsx +17 -0
  169. package/src/components/ui/overlay/Command.tsx +183 -0
  170. package/src/components/ui/overlay/Dialog.tsx +146 -0
  171. package/src/components/ui/overlay/Popover.tsx +47 -0
  172. package/src/components/ui/overlay/Sheet.tsx +145 -0
  173. package/src/components/ui/overlay/Tooltip.tsx +60 -0
  174. package/src/hooks/copy-to-clipboard.hook.tsx +52 -0
  175. package/src/index.ts +50 -0
  176. package/src/infra/api/client.ts +142 -0
  177. package/src/infra/api/types.ts +79 -0
  178. package/src/infra/utils/client-info.ts +35 -0
  179. package/src/infra/utils/clsx.ts +6 -0
  180. package/src/infra/utils/params.ts +27 -0
  181. package/src/infra/utils/percentage.ts +42 -0
  182. package/src/middlewares/chain.ts +45 -0
  183. package/src/middlewares/create-auth-middleware.ts +38 -0
  184. package/src/middlewares/types.ts +24 -0
  185. package/src/middlewares/validate-session.ts +116 -0
  186. package/src/modules/auth/actions/login.action.ts +11 -0
  187. package/src/modules/auth/actions/logout.action.ts +11 -0
  188. package/src/modules/auth/actions/register.action.ts +8 -0
  189. package/src/modules/auth/actions/validate-session.action.ts +51 -0
  190. package/src/modules/auth/hooks/login.hook.tsx +10 -0
  191. package/src/modules/auth/hooks/logout.hook.tsx +10 -0
  192. package/src/modules/auth/hooks/register.hook.tsx +10 -0
  193. package/src/modules/auth/hooks/useUserQuery.ts +49 -0
  194. package/src/modules/auth/schema.ts +220 -0
  195. package/src/modules/auth/services/auth.service.ts +225 -0
  196. package/src/modules/users/action/find-user-by-id.action.ts +25 -0
  197. package/src/modules/users/schema.ts +79 -0
  198. package/src/modules/users/services/user.service.ts +63 -0
  199. package/src/modules/whitelabel/actions/find-whitelabel.action.ts +20 -0
  200. package/src/modules/whitelabel/schema.ts +49 -0
  201. package/src/modules/whitelabel/services/whitelabel.service.ts +77 -0
  202. package/src/providers/auth.provider.tsx +113 -0
  203. package/src/providers/query.provider.tsx +52 -0
  204. package/src/providers/whitelabel.provider.tsx +44 -0
  205. package/src/store/useMdSidebarStore.ts +15 -0
@@ -0,0 +1,142 @@
1
+ import 'server-only';
2
+ import { ApiError } from './types';
3
+ import { findWhitelabel } from '../../modules/whitelabel/actions/find-whitelabel.action';
4
+
5
+ const API_BASE_URL = process.env.R3_API_URL || 'https://r3-api.greatapps.dev.br';
6
+ const API_VERSION = 'v1';
7
+ const API_LOCALE = 'pt-br';
8
+
9
+ interface RequestConfig extends RequestInit {
10
+ timeout?: number;
11
+ }
12
+
13
+ class ApiClient {
14
+ private async buildUrl(endpoint: string, whiteLabelId: number): Promise<string> {
15
+ const url = `${API_BASE_URL}/${API_VERSION}/${API_LOCALE}/${whiteLabelId}${endpoint}`;
16
+ console.log('[ApiClient] buildUrl', {
17
+ baseURL: API_BASE_URL,
18
+ whiteLabelId,
19
+ endpoint,
20
+ fullUrl: url,
21
+ });
22
+ return url;
23
+ }
24
+
25
+ private async getDefaultHeaders(token: string): Promise<HeadersInit> {
26
+ console.log('[ApiClient] getDefaultHeaders', {
27
+ token: token.substring(0, 20) + '...',
28
+ });
29
+
30
+ const headers: HeadersInit = {
31
+ 'Content-Type': 'application/json',
32
+ authorization: token,
33
+ };
34
+
35
+ return headers;
36
+ }
37
+
38
+ async request<T>(endpoint: string, config: RequestConfig = {}): Promise<T> {
39
+ console.log('[ApiClient] request called', { endpoint, method: config.method });
40
+
41
+ const { timeout = 30000, ...fetchConfig } = config;
42
+
43
+ const controller = new AbortController();
44
+ const timeoutId = setTimeout(() => controller.abort(), timeout);
45
+
46
+ try {
47
+ console.log('[ApiClient] Building URL and headers...');
48
+
49
+ const { id, token } = await findWhitelabel();
50
+
51
+ const [url, defaultHeaders] = await Promise.all([
52
+ this.buildUrl(endpoint, id),
53
+ this.getDefaultHeaders(token),
54
+ ]);
55
+
56
+ console.log('[ApiClient] Fetching', {
57
+ url,
58
+ method: fetchConfig.method,
59
+ headers: defaultHeaders,
60
+ });
61
+
62
+ const response = await fetch(url, {
63
+ ...fetchConfig,
64
+ headers: {
65
+ ...defaultHeaders,
66
+ ...fetchConfig.headers,
67
+ },
68
+ signal: controller.signal,
69
+ });
70
+
71
+ clearTimeout(timeoutId);
72
+
73
+ console.log('[ApiClient] Response received', { status: response.status, ok: response.ok });
74
+
75
+ if (!response.ok) {
76
+ const errorData = (await response.json().catch(() => ({}))) as Record<string, unknown>;
77
+ console.error('[ApiClient] Request failed', { status: response.status, errorData });
78
+ throw new ApiError(
79
+ (errorData.message as string) || `HTTP error ${response.status}`,
80
+ (errorData.code as string) || 'HTTP_ERROR',
81
+ response.status
82
+ );
83
+ }
84
+
85
+ const data = (await response.json()) as T;
86
+ console.log('[ApiClient] Request successful', { hasData: !!data });
87
+ return data;
88
+ } catch (error) {
89
+ clearTimeout(timeoutId);
90
+
91
+ console.error('[ApiClient] Request error', { error });
92
+
93
+ if (error instanceof ApiError) {
94
+ throw error;
95
+ }
96
+
97
+ if (error instanceof Error) {
98
+ if (error.name === 'AbortError') {
99
+ throw new ApiError('Tempo de requisição excedido', 'TIMEOUT', 408);
100
+ }
101
+ throw new ApiError(error.message, 'NETWORK_ERROR', 0);
102
+ }
103
+
104
+ throw new ApiError('Erro desconhecido', 'UNKNOWN_ERROR', 500);
105
+ }
106
+ }
107
+
108
+ async get<T>(endpoint: string, config?: RequestConfig): Promise<T> {
109
+ return this.request<T>(endpoint, { ...config, method: 'GET' });
110
+ }
111
+
112
+ async post<T>(endpoint: string, data?: unknown, config?: RequestConfig): Promise<T> {
113
+ return this.request<T>(endpoint, {
114
+ ...config,
115
+ method: 'POST',
116
+ body: data ? JSON.stringify(data) : undefined,
117
+ });
118
+ }
119
+
120
+ async put<T>(endpoint: string, data?: unknown, config?: RequestConfig): Promise<T> {
121
+ return this.request<T>(endpoint, {
122
+ ...config,
123
+ method: 'PUT',
124
+ body: data ? JSON.stringify(data) : undefined,
125
+ });
126
+ }
127
+
128
+ async patch<T>(endpoint: string, data?: unknown, config?: RequestConfig): Promise<T> {
129
+ return this.request<T>(endpoint, {
130
+ ...config,
131
+ method: 'PATCH',
132
+ body: data ? JSON.stringify(data) : undefined,
133
+ });
134
+ }
135
+
136
+ async delete<T>(endpoint: string, config?: RequestConfig): Promise<T> {
137
+ return this.request<T>(endpoint, { ...config, method: 'DELETE' });
138
+ }
139
+ }
140
+
141
+ export { ApiClient };
142
+ export const apiClient = new ApiClient();
@@ -0,0 +1,79 @@
1
+ import z from 'zod';
2
+
3
+ export const PaginationParamsSchema = z.object({
4
+ page: z.number().min(1).optional().default(20),
5
+ limit: z.number().optional().default(20),
6
+ });
7
+
8
+ export interface PaginationParams extends z.infer<typeof PaginationParamsSchema> {}
9
+
10
+ export const SearchParamsSchema = z.object({
11
+ search: z.string().optional(),
12
+ });
13
+
14
+ export interface SearchParams extends z.infer<typeof SearchParamsSchema> {}
15
+
16
+ export const SortParamsSchema = z.object({
17
+ sort: z.string().optional(),
18
+ });
19
+
20
+ export interface SortParams extends z.infer<typeof SortParamsSchema> {}
21
+
22
+ export interface ApiSuccessResult<T> {
23
+ status: 1;
24
+ data?: T;
25
+ }
26
+
27
+ export interface ApiPaginatedSuccessResult<T> extends Omit<ApiSuccessResult<T>, 'data'> {
28
+ data: T[];
29
+ total: number;
30
+ }
31
+
32
+ export interface ApiErrorResult {
33
+ status: 0;
34
+ message: string;
35
+ }
36
+
37
+ export type ApiActionResult<T> = ApiSuccessResult<T> | ApiErrorResult;
38
+ export type ApiPaginatedActionResult<T> = ApiPaginatedSuccessResult<T> | ApiErrorResult;
39
+
40
+ export interface SuccessResult<T> {
41
+ success: true;
42
+ data?: T;
43
+ total?: number;
44
+ }
45
+
46
+ export interface PaginatedSuccessResult<T> extends Omit<SuccessResult<T>, 'data'> {
47
+ data: T[];
48
+ total: number;
49
+ }
50
+
51
+ export interface ErrorResult {
52
+ success: false;
53
+ error?: string;
54
+ }
55
+
56
+ export type ActionResult<T> = SuccessResult<T> | ErrorResult;
57
+ export type PaginatedActionResult<T> = PaginatedSuccessResult<T> | ErrorResult;
58
+
59
+ export class ApiError extends Error {
60
+ public readonly code: string;
61
+ public readonly statusCode: number;
62
+
63
+ constructor(message: string, code: string = 'UNKNOWN_ERROR', statusCode: number = 500) {
64
+ super(message);
65
+ this.name = 'ApiError';
66
+ this.code = code;
67
+ this.statusCode = statusCode;
68
+ }
69
+
70
+ static isApiError(error: unknown): error is ApiError {
71
+ return error instanceof ApiError;
72
+ }
73
+ }
74
+
75
+ export interface ApiResponse<T = unknown> {
76
+ status: 0 | 1;
77
+ message?: string;
78
+ data?: T;
79
+ }
@@ -0,0 +1,35 @@
1
+ import { headers } from 'next/headers';
2
+ import { NextRequest } from 'next/server';
3
+ import { ClientInfo } from '../../modules/auth/schema';
4
+
5
+ export async function getClientInfoFromRequest(request?: NextRequest): Promise<ClientInfo> {
6
+ let headerStore: Headers;
7
+
8
+ if (request) {
9
+ // Middleware context
10
+ headerStore = request.headers;
11
+ } else {
12
+ // Server action context
13
+ const nextHeaders = await headers();
14
+ headerStore = nextHeaders as unknown as Headers;
15
+ }
16
+
17
+ return {
18
+ location: {
19
+ continent:
20
+ headerStore.get('cf-ipcountry') || headerStore.get('x-vercel-ip-country') || 'Unknown',
21
+ country: headerStore.get('x-vercel-ip-country') || 'Unknown',
22
+ region: headerStore.get('x-vercel-ip-country-region') || 'Unknown',
23
+ city: headerStore.get('x-vercel-ip-city') || 'Unknown',
24
+ latitude: headerStore.get('x-vercel-ip-latitude') || '0',
25
+ longitude: headerStore.get('x-vercel-ip-longitude') || '0',
26
+ },
27
+ ip:
28
+ headerStore.get('cf-connecting-ip') ||
29
+ headerStore.get('x-forwarded-for') ||
30
+ headerStore.get('x-real-ip') ||
31
+ 'Unknown',
32
+ timezone: headerStore.get('cf-timezone') || 'America/Sao_Paulo',
33
+ agent: headerStore.get('user-agent') || 'Unknown',
34
+ };
35
+ }
@@ -0,0 +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
+ }
@@ -0,0 +1,27 @@
1
+ export function buildQueryParams(
2
+ params?: Record<
3
+ string,
4
+ string | number | boolean | Date | null | undefined | string[] | number[] | boolean[] | Date[]
5
+ >
6
+ ) {
7
+ if (!params) {
8
+ return '';
9
+ }
10
+
11
+ const queryParams = new URLSearchParams();
12
+
13
+ for (const [key, value] of Object.entries(params)) {
14
+ if (value === null || value === undefined || value === '') {
15
+ continue;
16
+ }
17
+
18
+ if (Array.isArray(value)) {
19
+ queryParams.set(key, value.join(','));
20
+ } else {
21
+
22
+ queryParams.set(key, value.toString());
23
+ }
24
+ }
25
+
26
+ return queryParams.toString();
27
+ }
@@ -0,0 +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
+ };
@@ -0,0 +1,45 @@
1
+ import { NextRequest, NextResponse } from 'next/server';
2
+
3
+ import { MiddlewareConfig } from './types';
4
+
5
+ function shouldProcessPath(pathname: string, config: MiddlewareConfig): boolean {
6
+ const { excludePaths, includePaths } = config;
7
+
8
+ if (excludePaths?.length) {
9
+ const isExcluded = excludePaths.some(
10
+ (path) => pathname === path || pathname.startsWith(`${path}/`)
11
+ );
12
+ if (isExcluded) return false;
13
+ }
14
+
15
+ if (includePaths?.length) {
16
+ return includePaths.some((path) => pathname === path || pathname.startsWith(`${path}/`));
17
+ }
18
+
19
+ return true;
20
+ }
21
+
22
+ export function createMiddlewareChain(middlewares: MiddlewareConfig[]) {
23
+ return async function chainedMiddleware(request: NextRequest): Promise<NextResponse> {
24
+ const { pathname } = request.nextUrl;
25
+ let currentRequest = request;
26
+
27
+ for (const middleware of middlewares) {
28
+ if (!shouldProcessPath(pathname, middleware)) {
29
+ continue;
30
+ }
31
+
32
+ const result = await middleware.handler(currentRequest);
33
+
34
+ if (result.type === 'response') {
35
+ return result.response;
36
+ }
37
+
38
+ if (result.request) {
39
+ currentRequest = result.request;
40
+ }
41
+ }
42
+
43
+ return NextResponse.next();
44
+ };
45
+ }
@@ -0,0 +1,38 @@
1
+ import { NextRequest, NextResponse } from 'next/server';
2
+
3
+ import { continueChain, MiddlewareConfig, MiddlewareFunction, MiddlewareResult, stopChain } from './types';
4
+
5
+ function isRouteEquals(pathname: string, routes: string[]): boolean {
6
+ return routes.some((route) => pathname === route || pathname.startsWith(`${route}/`));
7
+ }
8
+
9
+ interface CreateAuthMiddlewareOptions {
10
+ publicRoutes?: string[];
11
+ authRoutes?: string[];
12
+ }
13
+
14
+ export function createAuthMiddleware(options?: CreateAuthMiddlewareOptions): MiddlewareFunction {
15
+ return (request: NextRequest) => {
16
+ const { pathname } = request.nextUrl;
17
+ const token = request.cookies.get('greatapps')?.value;
18
+ const isPublicRoute = isRouteEquals(pathname, options?.publicRoutes || []);
19
+ const isAuthRoute = isRouteEquals(pathname, options?.authRoutes || []);
20
+
21
+ if (token && isAuthRoute) {
22
+ return stopChain(NextResponse.redirect(new URL('/', request.url)));
23
+ }
24
+
25
+ if (!token && !isPublicRoute) {
26
+
27
+ const loginUrl = new URL('/login', request.url);
28
+
29
+ if (pathname !== '/') {
30
+ loginUrl.searchParams.set('redirect', pathname);
31
+ }
32
+
33
+ return stopChain(NextResponse.redirect(loginUrl));
34
+ }
35
+
36
+ return continueChain();
37
+ }
38
+ }
@@ -0,0 +1,24 @@
1
+ import { NextRequest, NextResponse } from 'next/server';
2
+
3
+ export type MiddlewareResult =
4
+ | { type: 'response'; response: NextResponse }
5
+ | { type: 'next'; request?: NextRequest };
6
+
7
+ export type MiddlewareFunction = (
8
+ request: NextRequest
9
+ ) => MiddlewareResult | Promise<MiddlewareResult>;
10
+
11
+ export interface MiddlewareConfig {
12
+ name: string;
13
+ handler: MiddlewareFunction;
14
+ excludePaths?: string[];
15
+ includePaths?: string[];
16
+ }
17
+
18
+ export function continueChain(request?: NextRequest): MiddlewareResult {
19
+ return { type: 'next', request };
20
+ }
21
+
22
+ export function stopChain(response: NextResponse): MiddlewareResult {
23
+ return { type: 'response', response };
24
+ }
@@ -0,0 +1,116 @@
1
+ 'use server';
2
+
3
+ import { redirect } from 'next/navigation';
4
+ import { SessionKeepRequest, SessionKeepResponse } from '../types/auth';
5
+ import { getWhitelabelServerData } from '../modules/whitelabel/whitelabel-token.server';
6
+ import { getClientInfoFromRequest } from '../infra/utils/client-info';
7
+ import { cookies, headers } from 'next/headers';
8
+
9
+ const API_BASE_URL = process.env.R3_API_URL || 'https://r3-api.greatapps.dev.br';
10
+ const API_VERSION = 'v1';
11
+ const API_LOCALE = 'pt-br';
12
+
13
+ async function redirectToLogin() {
14
+ const h = await headers();
15
+ const referer = h.get('referer') || '/';
16
+
17
+ if (!referer) {
18
+ redirect('/login');
19
+ return;
20
+ }
21
+
22
+ const url = new URL(referer);
23
+
24
+ if (['/login', '/register', '/forgot-password'].includes(url.pathname)) {
25
+ return;
26
+ }
27
+
28
+ redirect('/login');
29
+ }
30
+
31
+ export async function validateSession(): Promise<boolean> {
32
+ console.log('[validateSession] Starting session validation');
33
+
34
+ const cookieStore = await cookies();
35
+ const cookie = cookieStore.get('greatapps')?.value;
36
+
37
+ if (!cookie) {
38
+ console.log('[validateSession] No cookie found');
39
+ await redirectToLogin();
40
+ return false;
41
+ }
42
+
43
+ console.log('[validateSession] Cookie found:', cookie.substring(0, 30) + '...');
44
+
45
+ try {
46
+ // Busca dados do whitelabel
47
+ const whitelabel = await getWhitelabelServerData();
48
+
49
+ if (!whitelabel) {
50
+ console.error('[validateSession] Could not get whitelabel data');
51
+ await redirectToLogin();
52
+ return false;
53
+ }
54
+
55
+ console.log('[validateSession] Whitelabel data:', {
56
+ wlId: whitelabel.wlId,
57
+ hasToken: !!whitelabel.token,
58
+ });
59
+
60
+ const clientInfo = await getClientInfoFromRequest();
61
+
62
+ console.log('[validateSession] Client info:', {
63
+ ip: clientInfo.ip,
64
+ city: clientInfo.location.city,
65
+ country: clientInfo.location.country,
66
+ agent: clientInfo.agent.substring(0, 50) + '...',
67
+ });
68
+
69
+ const payload: SessionKeepRequest = {
70
+ location: clientInfo.location,
71
+ ip: clientInfo.ip,
72
+ timezone: clientInfo.timezone,
73
+ agent: clientInfo.agent,
74
+ cookie,
75
+ };
76
+
77
+ const url = `${API_BASE_URL}/${API_VERSION}/${API_LOCALE}/${whitelabel.wlId}/auth/keep`;
78
+
79
+ console.log('[validateSession] Calling API:', url);
80
+
81
+ const response = await fetch(url, {
82
+ method: 'POST',
83
+ headers: {
84
+ 'Content-Type': 'application/json',
85
+ authorization: whitelabel.token,
86
+ },
87
+ body: JSON.stringify(payload),
88
+ });
89
+
90
+ console.log('[validateSession] API response status:', response.status);
91
+
92
+ if (!response.ok) {
93
+ const errorText = await response.text();
94
+ console.error('[validateSession] Session validation failed', {
95
+ status: response.status,
96
+ error: errorText,
97
+ });
98
+ cookieStore.delete('greatapps');
99
+ await redirectToLogin();
100
+ return false;
101
+ }
102
+
103
+ const data = (await response.json()) as SessionKeepResponse;
104
+
105
+ console.log('[validateSession] API response data:', data);
106
+
107
+ const isValid = data.status === 1;
108
+
109
+ console.log('[validateSession] Session is valid:', isValid);
110
+ return isValid;
111
+ } catch (error) {
112
+ console.error('[validateSession] Error validating session', error);
113
+ await redirectToLogin();
114
+ return false;
115
+ }
116
+ }
@@ -0,0 +1,11 @@
1
+ 'use server';
2
+
3
+ import { authService } from '../services/auth.service';
4
+ import { LoginRequest, LoginResponse } from '../schema';
5
+ import { getClientInfoFromRequest } from '../../../infra/utils/client-info';
6
+
7
+ export async function loginAction(credentials: LoginRequest): Promise<LoginResponse> {
8
+ const clientInfo = await getClientInfoFromRequest();
9
+
10
+ return await authService.login(credentials, clientInfo)
11
+ }
@@ -0,0 +1,11 @@
1
+ 'use server';
2
+
3
+ import { getClientInfoFromRequest } from "../../../infra/utils/client-info";
4
+ import { LogoutResponse } from "../schema";
5
+ import { authService } from "../services/auth.service";
6
+
7
+ export async function logoutAction(): Promise<LogoutResponse> {
8
+ const clientInfo = await getClientInfoFromRequest();
9
+
10
+ return await authService.logout(clientInfo)
11
+ }
@@ -0,0 +1,8 @@
1
+ 'use server';
2
+
3
+ import { RegisterRequest, RegisterResponse } from "../schema";
4
+ import { authService } from "../services/auth.service";
5
+
6
+ export async function registerAction(data: RegisterRequest): Promise<RegisterResponse> {
7
+ return await authService.register(data)
8
+ }
@@ -0,0 +1,51 @@
1
+ 'use server';
2
+
3
+ import { redirect } from 'next/navigation';
4
+ import { cookies, headers } from 'next/headers';
5
+ import { authService } from '../services/auth.service';
6
+ import { getClientInfoFromRequest } from '../../../infra/utils/client-info';
7
+
8
+ async function redirectToLogin() {
9
+ const h = await headers();
10
+ const referer = h.get('referer') || '/';
11
+
12
+ if (!referer) {
13
+ redirect('/login');
14
+ return;
15
+ }
16
+
17
+ const url = new URL(referer);
18
+
19
+ if (['/login', '/register', '/forgot-password'].includes(url.pathname)) {
20
+ return;
21
+ }
22
+
23
+ redirect('/login');
24
+ }
25
+
26
+ export async function validateSessionAction(): Promise<boolean> {
27
+ const cookieStore = await cookies();
28
+ const cookie = cookieStore.get('greatapps')?.value;
29
+
30
+ if (!cookie) {
31
+ await redirectToLogin();
32
+ return false;
33
+ }
34
+
35
+ try {
36
+ const clientInfo = await getClientInfoFromRequest();
37
+ const isValid = await authService.validateSession(clientInfo);
38
+
39
+ if (!isValid) {
40
+ cookieStore.delete('greatapps');
41
+ await redirectToLogin();
42
+ return false;
43
+ }
44
+
45
+ return true;
46
+ } catch (error) {
47
+ console.error('[validateSessionAction] Error:', error);
48
+ await redirectToLogin();
49
+ return false;
50
+ }
51
+ }
@@ -0,0 +1,10 @@
1
+ "use client";
2
+
3
+ import { useMutation } from "@tanstack/react-query";
4
+ import { loginAction } from "../actions/login.action";
5
+
6
+ export function useLogin() {
7
+ return useMutation({
8
+ mutationFn: loginAction,
9
+ });
10
+ }
@@ -0,0 +1,10 @@
1
+ "use client";
2
+
3
+ import { useMutation } from "@tanstack/react-query";
4
+ import { logoutAction } from "../actions/logout.action";
5
+
6
+ export function useLogout() {
7
+ return useMutation({
8
+ mutationFn: logoutAction,
9
+ });
10
+ }
@@ -0,0 +1,10 @@
1
+ "use client";
2
+
3
+ import { useMutation } from "@tanstack/react-query";
4
+ import { registerAction } from "../actions/register.action";
5
+
6
+ export function useRegister() {
7
+ return useMutation({
8
+ mutationFn: registerAction,
9
+ });
10
+ }