warqadui 0.0.4 → 0.0.6

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 (179) hide show
  1. package/.vscode/settings.json +3 -0
  2. package/apps/dev-app/.env +1 -0
  3. package/apps/dev-app/errors.log +0 -0
  4. package/apps/dev-app/index.html +12 -0
  5. package/apps/dev-app/node_modules/.vite/deps/@tanstack_react-table.js +3254 -0
  6. package/apps/dev-app/node_modules/.vite/deps/@tanstack_react-table.js.map +7 -0
  7. package/apps/dev-app/node_modules/.vite/deps/_metadata.json +178 -0
  8. package/apps/dev-app/node_modules/.vite/deps/antd.js +108982 -0
  9. package/apps/dev-app/node_modules/.vite/deps/antd.js.map +7 -0
  10. package/apps/dev-app/node_modules/.vite/deps/axios.js +2751 -0
  11. package/apps/dev-app/node_modules/.vite/deps/axios.js.map +7 -0
  12. package/apps/dev-app/node_modules/.vite/deps/chunk-5OG7DCD7.js +41 -0
  13. package/apps/dev-app/node_modules/.vite/deps/chunk-5OG7DCD7.js.map +7 -0
  14. package/apps/dev-app/node_modules/.vite/deps/chunk-7YRZYZRE.js +7807 -0
  15. package/apps/dev-app/node_modules/.vite/deps/chunk-7YRZYZRE.js.map +7 -0
  16. package/apps/dev-app/node_modules/.vite/deps/chunk-DC5AMYBS.js +39 -0
  17. package/apps/dev-app/node_modules/.vite/deps/chunk-DC5AMYBS.js.map +7 -0
  18. package/apps/dev-app/node_modules/.vite/deps/chunk-DKXRQMOD.js +135 -0
  19. package/apps/dev-app/node_modules/.vite/deps/chunk-DKXRQMOD.js.map +7 -0
  20. package/apps/dev-app/node_modules/.vite/deps/chunk-EL47BWQR.js +37 -0
  21. package/apps/dev-app/node_modules/.vite/deps/chunk-EL47BWQR.js.map +7 -0
  22. package/apps/dev-app/node_modules/.vite/deps/chunk-HHL3MHGV.js +288 -0
  23. package/apps/dev-app/node_modules/.vite/deps/chunk-HHL3MHGV.js.map +7 -0
  24. package/apps/dev-app/node_modules/.vite/deps/chunk-IGGUWUPT.js +60 -0
  25. package/apps/dev-app/node_modules/.vite/deps/chunk-IGGUWUPT.js.map +7 -0
  26. package/apps/dev-app/node_modules/.vite/deps/chunk-IGXZPJXT.js +928 -0
  27. package/apps/dev-app/node_modules/.vite/deps/chunk-IGXZPJXT.js.map +7 -0
  28. package/apps/dev-app/node_modules/.vite/deps/chunk-L2GCM37S.js +21628 -0
  29. package/apps/dev-app/node_modules/.vite/deps/chunk-L2GCM37S.js.map +7 -0
  30. package/apps/dev-app/node_modules/.vite/deps/chunk-LDRT62EN.js +14806 -0
  31. package/apps/dev-app/node_modules/.vite/deps/chunk-LDRT62EN.js.map +7 -0
  32. package/apps/dev-app/node_modules/.vite/deps/chunk-M7DZDBHW.js +14 -0
  33. package/apps/dev-app/node_modules/.vite/deps/chunk-M7DZDBHW.js.map +7 -0
  34. package/apps/dev-app/node_modules/.vite/deps/chunk-S54SBVCU.js +1906 -0
  35. package/apps/dev-app/node_modules/.vite/deps/chunk-S54SBVCU.js.map +7 -0
  36. package/apps/dev-app/node_modules/.vite/deps/chunk-WFNHCR67.js +21 -0
  37. package/apps/dev-app/node_modules/.vite/deps/chunk-WFNHCR67.js.map +7 -0
  38. package/apps/dev-app/node_modules/.vite/deps/clsx.js +10 -0
  39. package/apps/dev-app/node_modules/.vite/deps/clsx.js.map +7 -0
  40. package/apps/dev-app/node_modules/.vite/deps/dayjs.js +6 -0
  41. package/apps/dev-app/node_modules/.vite/deps/dayjs.js.map +7 -0
  42. package/apps/dev-app/node_modules/.vite/deps/dayjs_plugin_customParseFormat.js +6 -0
  43. package/apps/dev-app/node_modules/.vite/deps/dayjs_plugin_customParseFormat.js.map +7 -0
  44. package/apps/dev-app/node_modules/.vite/deps/framer-motion.js +12388 -0
  45. package/apps/dev-app/node_modules/.vite/deps/framer-motion.js.map +7 -0
  46. package/apps/dev-app/node_modules/.vite/deps/html2canvas-pro.js +9713 -0
  47. package/apps/dev-app/node_modules/.vite/deps/html2canvas-pro.js.map +7 -0
  48. package/apps/dev-app/node_modules/.vite/deps/html2canvas.esm-VL7GM4AH.js +8 -0
  49. package/apps/dev-app/node_modules/.vite/deps/html2canvas.esm-VL7GM4AH.js.map +7 -0
  50. package/apps/dev-app/node_modules/.vite/deps/index.es-3WTXOFZ2.js +10392 -0
  51. package/apps/dev-app/node_modules/.vite/deps/index.es-3WTXOFZ2.js.map +7 -0
  52. package/apps/dev-app/node_modules/.vite/deps/jspdf.js +41 -0
  53. package/apps/dev-app/node_modules/.vite/deps/jspdf.js.map +7 -0
  54. package/apps/dev-app/node_modules/.vite/deps/lucide-react.js +31586 -0
  55. package/apps/dev-app/node_modules/.vite/deps/lucide-react.js.map +7 -0
  56. package/apps/dev-app/node_modules/.vite/deps/package.json +3 -0
  57. package/apps/dev-app/node_modules/.vite/deps/purify.es-JNLDEIMX.js +1029 -0
  58. package/apps/dev-app/node_modules/.vite/deps/purify.es-JNLDEIMX.js.map +7 -0
  59. package/apps/dev-app/node_modules/.vite/deps/react-dom.js +7 -0
  60. package/apps/dev-app/node_modules/.vite/deps/react-dom.js.map +7 -0
  61. package/apps/dev-app/node_modules/.vite/deps/react-dom_client.js +8 -0
  62. package/apps/dev-app/node_modules/.vite/deps/react-dom_client.js.map +7 -0
  63. package/apps/dev-app/node_modules/.vite/deps/react-hook-form.js +2233 -0
  64. package/apps/dev-app/node_modules/.vite/deps/react-hook-form.js.map +7 -0
  65. package/apps/dev-app/node_modules/.vite/deps/react-phone-number-input.js +9307 -0
  66. package/apps/dev-app/node_modules/.vite/deps/react-phone-number-input.js.map +7 -0
  67. package/apps/dev-app/node_modules/.vite/deps/react-router-dom.js +14234 -0
  68. package/apps/dev-app/node_modules/.vite/deps/react-router-dom.js.map +7 -0
  69. package/apps/dev-app/node_modules/.vite/deps/react-to-pdf.js +268 -0
  70. package/apps/dev-app/node_modules/.vite/deps/react-to-pdf.js.map +7 -0
  71. package/apps/dev-app/node_modules/.vite/deps/react.js +6 -0
  72. package/apps/dev-app/node_modules/.vite/deps/react.js.map +7 -0
  73. package/apps/dev-app/node_modules/.vite/deps/react_jsx-dev-runtime.js +913 -0
  74. package/apps/dev-app/node_modules/.vite/deps/react_jsx-dev-runtime.js.map +7 -0
  75. package/apps/dev-app/node_modules/.vite/deps/react_jsx-runtime.js +7 -0
  76. package/apps/dev-app/node_modules/.vite/deps/react_jsx-runtime.js.map +7 -0
  77. package/apps/dev-app/node_modules/.vite/deps/tailwind-merge.js +2534 -0
  78. package/apps/dev-app/node_modules/.vite/deps/tailwind-merge.js.map +7 -0
  79. package/apps/dev-app/node_modules/tailwindcss/LICENSE +21 -0
  80. package/apps/dev-app/node_modules/tailwindcss/README.md +36 -0
  81. package/apps/dev-app/node_modules/tailwindcss/dist/chunk-L5IEUH3R.mjs +38 -0
  82. package/apps/dev-app/node_modules/tailwindcss/dist/chunk-UWKE2Z6N.mjs +1 -0
  83. package/apps/dev-app/node_modules/tailwindcss/dist/chunk-X4GG3EDV.mjs +1 -0
  84. package/apps/dev-app/node_modules/tailwindcss/dist/colors-C__qRT83.d.ts +347 -0
  85. package/apps/dev-app/node_modules/tailwindcss/dist/colors.d.mts +347 -0
  86. package/apps/dev-app/node_modules/tailwindcss/dist/colors.d.ts +5 -0
  87. package/apps/dev-app/node_modules/tailwindcss/dist/colors.js +1 -0
  88. package/apps/dev-app/node_modules/tailwindcss/dist/colors.mjs +1 -0
  89. package/apps/dev-app/node_modules/tailwindcss/dist/default-theme.d.mts +1199 -0
  90. package/apps/dev-app/node_modules/tailwindcss/dist/default-theme.d.ts +1199 -0
  91. package/apps/dev-app/node_modules/tailwindcss/dist/default-theme.js +1 -0
  92. package/apps/dev-app/node_modules/tailwindcss/dist/default-theme.mjs +1 -0
  93. package/apps/dev-app/node_modules/tailwindcss/dist/flatten-color-palette.d.mts +6 -0
  94. package/apps/dev-app/node_modules/tailwindcss/dist/flatten-color-palette.d.ts +6 -0
  95. package/apps/dev-app/node_modules/tailwindcss/dist/flatten-color-palette.js +3 -0
  96. package/apps/dev-app/node_modules/tailwindcss/dist/flatten-color-palette.mjs +1 -0
  97. package/apps/dev-app/node_modules/tailwindcss/dist/lib.d.mts +378 -0
  98. package/apps/dev-app/node_modules/tailwindcss/dist/lib.d.ts +3 -0
  99. package/apps/dev-app/node_modules/tailwindcss/dist/lib.js +38 -0
  100. package/apps/dev-app/node_modules/tailwindcss/dist/lib.mjs +1 -0
  101. package/apps/dev-app/node_modules/tailwindcss/dist/plugin.d.mts +11 -0
  102. package/apps/dev-app/node_modules/tailwindcss/dist/plugin.d.ts +134 -0
  103. package/apps/dev-app/node_modules/tailwindcss/dist/plugin.js +1 -0
  104. package/apps/dev-app/node_modules/tailwindcss/dist/plugin.mjs +1 -0
  105. package/apps/dev-app/node_modules/tailwindcss/dist/resolve-config-B4yBzhca.d.ts +29 -0
  106. package/apps/dev-app/node_modules/tailwindcss/dist/resolve-config-QUZ9b-Gn.d.mts +190 -0
  107. package/apps/dev-app/node_modules/tailwindcss/dist/types-CJYAW1ql.d.mts +128 -0
  108. package/apps/dev-app/node_modules/tailwindcss/index.css +944 -0
  109. package/apps/dev-app/node_modules/tailwindcss/package.json +89 -0
  110. package/apps/dev-app/node_modules/tailwindcss/preflight.css +393 -0
  111. package/apps/dev-app/node_modules/tailwindcss/theme.css +510 -0
  112. package/apps/dev-app/node_modules/tailwindcss/utilities.css +1 -0
  113. package/apps/dev-app/package.json +35 -0
  114. package/apps/dev-app/src/App.tsx +74 -0
  115. package/apps/dev-app/src/index.css +18 -0
  116. package/apps/dev-app/src/main.tsx +18 -0
  117. package/apps/dev-app/src/pages/Buttons.tsx +122 -0
  118. package/apps/dev-app/src/pages/DataTable.tsx +208 -0
  119. package/apps/dev-app/src/pages/Fields.tsx +342 -0
  120. package/apps/dev-app/src/pages/Modals.tsx +151 -0
  121. package/apps/dev-app/src/pages/Spins.tsx +161 -0
  122. package/apps/dev-app/ts_errors.txt +0 -0
  123. package/apps/dev-app/tsconfig.json +25 -0
  124. package/apps/dev-app/tsconfig.node.json +10 -0
  125. package/apps/dev-app/vite.config.ts +11 -0
  126. package/package.json +10 -49
  127. package/packages/ui/dist/index.d.mts +356 -0
  128. package/packages/ui/dist/index.d.ts +356 -0
  129. package/packages/ui/dist/index.js +2296 -0
  130. package/packages/ui/dist/index.mjs +2249 -0
  131. package/packages/ui/dist/styles.js +26 -0
  132. package/packages/ui/dist/styles.mjs +24 -0
  133. package/packages/ui/log.txt +0 -0
  134. package/packages/ui/package.json +68 -0
  135. package/packages/ui/postcss.config.js +6 -0
  136. package/packages/ui/src/components/Button.tsx +85 -0
  137. package/packages/ui/src/components/Card.tsx +97 -0
  138. package/packages/ui/src/components/CodeBlock.tsx +53 -0
  139. package/packages/ui/src/components/DashboardLayout.tsx +442 -0
  140. package/packages/ui/src/components/Fields/Input.tsx +191 -0
  141. package/packages/ui/src/components/Fields/PhoneInput.tsx +134 -0
  142. package/packages/ui/src/components/Fields/date.tsx +165 -0
  143. package/packages/ui/src/components/Fields/index.tsx +17 -0
  144. package/packages/ui/src/components/Fields/searchApi.tsx +479 -0
  145. package/packages/ui/src/components/Fields/select.tsx +131 -0
  146. package/packages/ui/src/components/Fields/textArea.tsx +121 -0
  147. package/packages/ui/src/components/LoadingBox.tsx +11 -0
  148. package/packages/ui/src/components/PageHeader.tsx +34 -0
  149. package/packages/ui/src/components/ThemeToggle.tsx +35 -0
  150. package/packages/ui/src/components/modal/Modal.tsx +81 -0
  151. package/packages/ui/src/components/spins/ClassicSpin.tsx +18 -0
  152. package/packages/ui/src/components/spins/LoadingSpin.tsx +45 -0
  153. package/packages/ui/src/components/spins/OverlaySpin.tsx +10 -0
  154. package/packages/ui/src/components/spins/index.tsx +13 -0
  155. package/packages/ui/src/components/tables/DataTable.tsx +261 -0
  156. package/packages/ui/src/components/tables/index.ts +1 -0
  157. package/packages/ui/src/hooks/Fetches/useApis.tsx +197 -0
  158. package/packages/ui/src/hooks/ThemeContext.tsx +56 -0
  159. package/packages/ui/src/hooks/useModal.tsx +38 -0
  160. package/packages/ui/src/hooks/useTheme.ts +34 -0
  161. package/packages/ui/src/index.ts +24 -0
  162. package/packages/ui/src/providers/WarqadProvider.tsx +69 -0
  163. package/packages/ui/src/styles.css +26 -0
  164. package/packages/ui/src/utils/cn.ts +6 -0
  165. package/packages/ui/src/utils/pdf.ts +171 -0
  166. package/packages/ui/tailwind.config.js +13 -0
  167. package/packages/ui/tsconfig.json +17 -0
  168. package/packages/ui/warqad-ui-0.0.1.tgz +0 -0
  169. package/packages/ui/warqadui-0.0.3.tgz +0 -0
  170. package/warqad-ui-0.0.1.tgz +0 -0
  171. package/dist/index.d.mts +0 -35
  172. package/dist/index.d.ts +0 -35
  173. package/dist/index.js +0 -470
  174. package/dist/index.mjs +0 -440
  175. package/dist/styles.js +0 -26
  176. package/dist/styles.mjs +0 -24
  177. /package/{dist → packages/ui/dist}/index.css +0 -0
  178. /package/{dist → packages/ui/dist}/styles.d.mts +0 -0
  179. /package/{dist → packages/ui/dist}/styles.d.ts +0 -0
@@ -0,0 +1,197 @@
1
+ import { useState, useMemo } from "react";
2
+ import axios from "axios";
3
+ import { useWarqadConfig } from "../../providers/WarqadProvider";
4
+
5
+ interface FetchProps {
6
+ url: string;
7
+ body?: Record<string, any>;
8
+ v?: 1 | 2;
9
+ params?: Record<string, any>;
10
+ delay?: number;
11
+ form?: {
12
+ setError: (field: string, error: { message: string }) => void;
13
+ };
14
+ multipart?: boolean;
15
+ }
16
+
17
+ export type FetchFunction = (props: Omit<FetchProps, "body">) => Promise<any>;
18
+ export type PostFunction = (props: FetchProps) => Promise<any>;
19
+ export type PutFunction = (props: FetchProps) => Promise<any>;
20
+ export type DeleteFunction = (props: Omit<FetchProps, "body">) => Promise<any>;
21
+
22
+ const useApi = () => {
23
+ const { apiUrl } = useWarqadConfig();
24
+
25
+ const api = useMemo(
26
+ () =>
27
+ axios.create({
28
+ baseURL: apiUrl || "/api",
29
+ withCredentials: true,
30
+ }),
31
+ [apiUrl],
32
+ );
33
+
34
+ const [data, setData] = useState<any>();
35
+ const [isLoading, setIsLoading] = useState(false);
36
+ const [pdfLoading, setPdfLoading] = useState(false);
37
+ const [error, setError] = useState<string | null>(null);
38
+
39
+ const get: FetchFunction = async ({ url, v = 1, params, delay }) => {
40
+ setIsLoading(true);
41
+ try {
42
+ if (delay) await new Promise((r) => setTimeout(r, delay));
43
+ const response = await api.get(`/v${v}${url}`, { params });
44
+ setData(response.data);
45
+ setError(null);
46
+ return response.data;
47
+ } catch (err: any) {
48
+ const msg =
49
+ err.response?.data?.message || err.message || "Something went wrong";
50
+ setError(msg);
51
+ setData(null);
52
+ throw new Error(msg);
53
+ } finally {
54
+ setIsLoading(false);
55
+ }
56
+ };
57
+
58
+ const post: PostFunction = async ({
59
+ url,
60
+ v = 1,
61
+ body,
62
+ form,
63
+ multipart = false,
64
+ }) => {
65
+ setIsLoading(true);
66
+ try {
67
+ let response;
68
+ if (multipart) {
69
+ const formData = new FormData();
70
+ if (body) {
71
+ Object.keys(body).forEach((key) => {
72
+ if (key === "files") {
73
+ const files = body[key];
74
+ if (Array.isArray(files)) {
75
+ files.forEach((file: File) => formData.append(key, file));
76
+ } else {
77
+ formData.append(key, files);
78
+ }
79
+ } else if (
80
+ Array.isArray(body[key]) ||
81
+ typeof body[key] === "object"
82
+ ) {
83
+ formData.append(key, JSON.stringify(body[key]));
84
+ } else {
85
+ formData.append(key, body[key]);
86
+ }
87
+ });
88
+ }
89
+ response = await api.post(`/v${v}${url}`, formData, {
90
+ headers: { "Content-Type": "multipart/form-data" },
91
+ });
92
+ } else {
93
+ response = await api.post(`/v${v}${url}`, body);
94
+ }
95
+ setData(response.data);
96
+ return response.data;
97
+ } catch (err: any) {
98
+ const msg =
99
+ err.response?.data?.message || err.message || "Something went wrong";
100
+ setError(msg);
101
+ if (form) {
102
+ if (msg !== "zodError") {
103
+ form.setError("root", { message: msg });
104
+ } else {
105
+ const errors = err?.response?.data?.errors || [];
106
+ errors.forEach((e: any) => {
107
+ form.setError(e.field, { message: e.message });
108
+ });
109
+ }
110
+ }
111
+ throw new Error(msg);
112
+ } finally {
113
+ setIsLoading(false);
114
+ }
115
+ };
116
+
117
+ const put: PutFunction = async ({ url, v = 1, body }) => {
118
+ setIsLoading(true);
119
+ try {
120
+ const response = await api.put(`/v${v}${url}`, body);
121
+ setData(response.data);
122
+ return response.data;
123
+ } catch (err: any) {
124
+ const msg =
125
+ err.response?.data?.message || err.message || "Something went wrong";
126
+ setError(msg);
127
+ throw new Error(msg);
128
+ } finally {
129
+ setIsLoading(false);
130
+ }
131
+ };
132
+
133
+ const remove: DeleteFunction = async ({ url, v = 1, params }) => {
134
+ setIsLoading(true);
135
+ try {
136
+ const response = await api.delete(`/v${v}${url}`, { params });
137
+ setData(response.data);
138
+ setError(null);
139
+ return response.data;
140
+ } catch (err: any) {
141
+ const msg =
142
+ err.response?.data?.message || err.message || "Something went wrong";
143
+ setError(msg);
144
+ setData(null);
145
+ throw new Error(msg);
146
+ } finally {
147
+ setIsLoading(false);
148
+ }
149
+ };
150
+
151
+ const fetchPdf = async ({
152
+ url,
153
+ v = 1,
154
+ method,
155
+ body,
156
+ }: FetchProps & { method: "GET" | "POST" }) => {
157
+ setPdfLoading(true);
158
+ try {
159
+ const response = await api.request({
160
+ url: `/v${v}${url}`,
161
+ method,
162
+ data: body,
163
+ responseType: "blob",
164
+ });
165
+ if (!response.data || response.data.size === 0) {
166
+ return { ok: false, message: "Received empty PDF file", errors: [] };
167
+ }
168
+ setError(null);
169
+ const pdfObjectUrl = URL.createObjectURL(response.data);
170
+ return { ok: true, data: pdfObjectUrl };
171
+ } catch (err: any) {
172
+ return {
173
+ ok: false,
174
+ message:
175
+ err.response?.data?.message ||
176
+ err.message ||
177
+ "Server error, something went wrong",
178
+ };
179
+ } finally {
180
+ setPdfLoading(false);
181
+ }
182
+ };
183
+
184
+ return {
185
+ data,
186
+ error,
187
+ isLoading,
188
+ pdfLoading,
189
+ get,
190
+ post,
191
+ put,
192
+ remove,
193
+ fetchPdf,
194
+ };
195
+ };
196
+
197
+ export default useApi;
@@ -0,0 +1,56 @@
1
+ import React, {
2
+ createContext,
3
+ useContext,
4
+ useState,
5
+ useEffect,
6
+ ReactNode,
7
+ } from "react";
8
+
9
+ interface ThemeContextType {
10
+ isDarkMode: boolean;
11
+ toggleTheme: () => void;
12
+ setTheme: (theme: "light" | "dark") => void;
13
+ }
14
+
15
+ const ThemeContext = createContext<ThemeContextType | undefined>(undefined);
16
+
17
+ export const ThemeProvider = ({ children }: { children: ReactNode }) => {
18
+ const [isDarkMode, setIsDarkMode] = useState(() => {
19
+ if (typeof window !== "undefined") {
20
+ const savedTheme = localStorage.getItem("theme");
21
+ if (savedTheme) {
22
+ return savedTheme === "dark";
23
+ }
24
+ return window.matchMedia("(prefers-color-scheme: dark)").matches;
25
+ }
26
+ return false;
27
+ });
28
+
29
+ useEffect(() => {
30
+ const root = window.document.documentElement;
31
+ if (isDarkMode) {
32
+ root.classList.add("dark");
33
+ localStorage.setItem("theme", "dark");
34
+ } else {
35
+ root.classList.remove("dark");
36
+ localStorage.setItem("theme", "light");
37
+ }
38
+ }, [isDarkMode]);
39
+
40
+ const toggleTheme = () => setIsDarkMode((prev) => !prev);
41
+ const setTheme = (theme: "light" | "dark") => setIsDarkMode(theme === "dark");
42
+
43
+ return (
44
+ <ThemeContext.Provider value={{ isDarkMode, toggleTheme, setTheme }}>
45
+ {children}
46
+ </ThemeContext.Provider>
47
+ );
48
+ };
49
+
50
+ export const useTheme = () => {
51
+ const context = useContext(ThemeContext);
52
+ if (context === undefined) {
53
+ throw new Error("useTheme must be used within a ThemeProvider");
54
+ }
55
+ return context;
56
+ };
@@ -0,0 +1,38 @@
1
+ import { useState, useCallback } from "react";
2
+ import { Modal, ModalProps } from "../components/modal/Modal";
3
+
4
+ export interface UseModalReturn {
5
+ isOpen: boolean;
6
+ open: () => void;
7
+ close: () => void;
8
+ toggle: () => void;
9
+ openState: (data: any) => void;
10
+ state: any;
11
+ Modal: React.FC<ModalProps>;
12
+ }
13
+
14
+ export const useModal = (initialState = false): UseModalReturn => {
15
+ const [isOpen, setIsOpen] = useState(initialState);
16
+ const [state, setState] = useState(null);
17
+ const open = useCallback(() => setIsOpen(true), []);
18
+ const close = useCallback(() => {
19
+ setIsOpen(false);
20
+ setState(null);
21
+ }, []);
22
+ const toggle = useCallback(() => setIsOpen((prev) => !prev), []);
23
+ const openState = useCallback((data: any) => {
24
+ setState(data);
25
+ open();
26
+ }, []);
27
+ return {
28
+ isOpen,
29
+ open,
30
+ close,
31
+ toggle,
32
+ openState,
33
+ state,
34
+ Modal: Modal,
35
+ };
36
+ };
37
+
38
+ export default useModal;
@@ -0,0 +1,34 @@
1
+ import { useState, useEffect } from "react";
2
+
3
+ const useTheme = () => {
4
+ // Check local storage or system preference on mount
5
+ const [isDarkMode, setIsDarkMode] = useState(() => {
6
+ if (typeof window !== "undefined") {
7
+ const savedTheme = localStorage.getItem("theme");
8
+ if (savedTheme) {
9
+ return savedTheme === "dark";
10
+ }
11
+ return window.matchMedia("(prefers-color-scheme: dark)").matches;
12
+ }
13
+ return false;
14
+ });
15
+
16
+ useEffect(() => {
17
+ const root = window.document.documentElement;
18
+ if (isDarkMode) {
19
+ root.classList.add("dark");
20
+ localStorage.setItem("theme", "dark");
21
+ } else {
22
+ root.classList.remove("dark");
23
+ localStorage.setItem("theme", "light");
24
+ }
25
+ }, [isDarkMode]);
26
+
27
+ const toggleTheme = () => {
28
+ setIsDarkMode((prev) => !prev);
29
+ };
30
+
31
+ return { isDarkMode, toggleTheme };
32
+ };
33
+
34
+ export default useTheme;
@@ -0,0 +1,24 @@
1
+ export * from "./components/Button";
2
+ export * from "./components/DashboardLayout";
3
+ export * from "./components/ThemeToggle";
4
+ export * from "./hooks/ThemeContext";
5
+ export * from "./hooks/useTheme";
6
+ export * from "./components/LoadingBox";
7
+ export * from "./components/spins";
8
+ export * from "./components/CodeBlock";
9
+ export * from "./components/Card";
10
+ export * from "./components/modal/Modal";
11
+ export * from "./components/PageHeader";
12
+ export * from "./hooks/useModal";
13
+ export { default as Fields } from "./components/Fields";
14
+ export * from "./components/Fields/Input";
15
+ export * from "./components/Fields/PhoneInput";
16
+ export * from "./components/Fields/select";
17
+ export * from "./components/Fields/textArea";
18
+ export * from "./components/Fields/searchApi";
19
+ export * from "./components/Fields/date";
20
+ export * from "./components/tables";
21
+
22
+ export * from "./providers/WarqadProvider";
23
+ export { default as useApi } from "./hooks/Fetches/useApis";
24
+ export * from "./utils/pdf";
@@ -0,0 +1,69 @@
1
+ import React, { createContext, useContext, ReactNode } from "react";
2
+ import { ThemeProvider } from "../hooks/ThemeContext";
3
+
4
+ export interface ThemeConfig {
5
+ primaryColor?: string;
6
+ // System is extendable for later:
7
+ // borderRadius?: string;
8
+ // fontSize?: string;
9
+ }
10
+
11
+ export interface WarqadConfig {
12
+ apiUrl?: string;
13
+ theme?: ThemeConfig;
14
+ // System is extendable for later:
15
+ // auth?: AuthConfig;
16
+ // featureFlags?: Record<string, boolean>;
17
+ }
18
+
19
+ export interface WarqadConfigContextType {
20
+ config: WarqadConfig;
21
+ }
22
+
23
+ const WarqadConfigContext = createContext<WarqadConfigContextType | undefined>(
24
+ undefined,
25
+ );
26
+
27
+ export const useWarqadConfig = () => {
28
+ const context = useContext(WarqadConfigContext);
29
+ if (context === undefined) {
30
+ throw new Error("useWarqadConfig must be used within a WarqadProvider");
31
+ }
32
+ return context.config;
33
+ };
34
+
35
+ interface WarqadProviderProps {
36
+ children: ReactNode;
37
+ config?: WarqadConfig;
38
+ }
39
+
40
+ export const WarqadProvider = ({
41
+ children,
42
+ config = { apiUrl: "/api" },
43
+ }: WarqadProviderProps) => {
44
+ // Merge default config with provided config
45
+ const mergedConfig: WarqadConfig = {
46
+ apiUrl: "/api",
47
+ ...config,
48
+ theme: {
49
+ primaryColor: "#3b82f6", // Default blue-500
50
+ ...config?.theme,
51
+ },
52
+ };
53
+
54
+ return (
55
+ <WarqadConfigContext.Provider value={{ config: mergedConfig }}>
56
+ <ThemeProvider>
57
+ <div
58
+ style={
59
+ {
60
+ "--primary-color": mergedConfig.theme?.primaryColor,
61
+ } as React.CSSProperties
62
+ }
63
+ >
64
+ {children}
65
+ </div>
66
+ </ThemeProvider>
67
+ </WarqadConfigContext.Provider>
68
+ );
69
+ };
@@ -0,0 +1,26 @@
1
+ @import url("https://fonts.googleapis.com/css2?family=Inter:wght@100;200;300;400;500;600;700;800;900&display=swap");
2
+
3
+ @tailwind base;
4
+ @tailwind components;
5
+ @tailwind utilities;
6
+
7
+ @layer base {
8
+ :root {
9
+ font-family:
10
+ "Inter",
11
+ system-ui,
12
+ -apple-system,
13
+ sans-serif;
14
+ }
15
+
16
+ body {
17
+ @apply antialiased text-slate-900 dark:text-slate-50;
18
+ font-feature-settings: "cv02", "cv03", "cv04", "cv11";
19
+ }
20
+
21
+ *,
22
+ *::before,
23
+ *::after {
24
+ @apply transition-colors duration-500 ease-in-out;
25
+ }
26
+ }
@@ -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,171 @@
1
+ import html2canvas from "html2canvas-pro";
2
+ import { jsPDF } from "jspdf";
3
+
4
+ export interface PdfOptions {
5
+ orientation?: "portrait" | "landscape";
6
+ unit?: "mm" | "cm" | "in" | "px";
7
+ format?: string | number[];
8
+ filename?: string;
9
+ margin?: number;
10
+ windowWidth?: number;
11
+ scale?: number;
12
+ showPageNumbers?: boolean;
13
+ }
14
+
15
+ /**
16
+ * Generates a text-based, searchable PDF from a DOM element.
17
+ *
18
+ * @param elementId The ID of the element to capture
19
+ * @param options PDF generation options
20
+ */
21
+ export const generatePdf = async (
22
+ elementId: string,
23
+ options: PdfOptions = {},
24
+ ) => {
25
+ const {
26
+ orientation = "landscape",
27
+ unit = "mm",
28
+ format = "a4",
29
+ filename = "export.pdf",
30
+ margin = 10,
31
+ windowWidth = 1200,
32
+ scale = 1,
33
+ showPageNumbers = true,
34
+ } = options;
35
+
36
+ const element = document.getElementById(elementId);
37
+ if (!element) {
38
+ console.error(
39
+ `PDF generation failed: Element with ID "${elementId}" not found.`,
40
+ );
41
+ return;
42
+ }
43
+
44
+ try {
45
+ const pdf = new jsPDF({
46
+ orientation,
47
+ unit,
48
+ format,
49
+ compress: true,
50
+ });
51
+
52
+ const pageWidth = pdf.internal.pageSize.getWidth();
53
+ const pageHeight = pdf.internal.pageSize.getHeight();
54
+
55
+ // Ensure html2canvas-pro is globally available for jsPDF's internal calls
56
+ (window as any).html2canvas = html2canvas;
57
+
58
+ await pdf.html(element, {
59
+ callback: function (doc) {
60
+ if (showPageNumbers) {
61
+ const pageCount = (doc.internal as any).getNumberOfPages();
62
+ for (let i = 1; i <= pageCount; i++) {
63
+ doc.setPage(i);
64
+ doc.setFontSize(8); // Changed from 10 to 8
65
+ doc.setTextColor(150);
66
+ const text = `Page ${i} of ${pageCount}`;
67
+ const textWidth =
68
+ (doc.getStringUnitWidth(text) * 8) / doc.internal.scaleFactor; // Changed from 10 to 8
69
+ doc.text(text, (pageWidth - textWidth) / 2, pageHeight - 5); // Changed from -10 to -5
70
+ }
71
+ }
72
+
73
+ doc.save(filename);
74
+
75
+ // Open in new tab
76
+ const pdfBlob = doc.output("blob");
77
+ const url = URL.createObjectURL(pdfBlob);
78
+ window.open(url, "_blank");
79
+ },
80
+ x: margin,
81
+ y: margin,
82
+ width: pageWidth - margin * 2,
83
+ windowWidth: windowWidth,
84
+ html2canvas: {
85
+ useCORS: true,
86
+ scale: Math.max(scale, 2), // Ensure scale is at least 2 for text clarity
87
+ logging: false,
88
+ onclone: (clonedDoc: Document) => {
89
+ const elements = clonedDoc.getElementsByTagName("*");
90
+ const tempCanvas = clonedDoc.createElement("canvas");
91
+ tempCanvas.width = 1;
92
+ tempCanvas.height = 1;
93
+ const ctx = tempCanvas.getContext("2d");
94
+
95
+ // Ensure the target element takes the full window width in the clone
96
+ const target = clonedDoc.getElementById(elementId);
97
+ if (target) {
98
+ target.style.width = `${windowWidth}px`;
99
+ target.style.maxWidth = "none";
100
+ }
101
+
102
+ const toRgb = (color: string) => {
103
+ if (
104
+ !color ||
105
+ (!color.includes("oklch") && !color.includes("oklab"))
106
+ )
107
+ return color;
108
+ if (!ctx) return "rgb(0,0,0)";
109
+ try {
110
+ ctx.fillStyle = color;
111
+ const result = ctx.fillStyle;
112
+ // If the browser returns oklch/oklab even from canvas, force black
113
+ return result.includes("ok") ? "rgb(0,0,0)" : result;
114
+ } catch (e) {
115
+ return "rgb(0,0,0)";
116
+ }
117
+ };
118
+
119
+ // 1. Bake all computed colors as standard RGB into every element
120
+ for (let i = 0; i < elements.length; i++) {
121
+ const el = elements[i] as HTMLElement;
122
+ // Important: Use original window style as clone isn't rendered yet
123
+ const style = window.getComputedStyle(elements[i]);
124
+
125
+ const colorProps = [
126
+ "color",
127
+ "backgroundColor",
128
+ "borderColor",
129
+ "borderTopColor",
130
+ "borderBottomColor",
131
+ "borderLeftColor",
132
+ "borderRightColor",
133
+ "fill",
134
+ "stroke",
135
+ "outlineColor",
136
+ "stopColor",
137
+ "floodColor",
138
+ "lightingColor",
139
+ ];
140
+
141
+ colorProps.forEach((prop) => {
142
+ const val = (style as any)[prop];
143
+ if (val && (val.includes("oklch") || val.includes("oklab"))) {
144
+ (el.style as any)[prop] = toRgb(val);
145
+ }
146
+ });
147
+ }
148
+
149
+ // 2. Aggressively strip oklch/oklab from all style tags to prevent parser crash
150
+ const styleTags = clonedDoc.getElementsByTagName("style");
151
+ for (let i = 0; i < styleTags.length; i++) {
152
+ let cssText = styleTags[i].innerHTML;
153
+ if (cssText.includes("oklch") || cssText.includes("oklab")) {
154
+ cssText = cssText.replace(/oklch\([^)]+\)/g, "rgb(0,0,0)");
155
+ cssText = cssText.replace(/oklab\([^)]+\)/g, "rgb(0,0,0)");
156
+ styleTags[i].innerHTML = cssText;
157
+ }
158
+ }
159
+
160
+ // 3. Remove external stylesheets that might contain oklch variables
161
+ // (Since we baked computed styles into elements, we only need basic layout CSS)
162
+ // We'll keep them but try to sanitize if they are accessible (though usually not)
163
+ },
164
+ } as any,
165
+ autoPaging: "text",
166
+ });
167
+ } catch (error) {
168
+ console.error("PDF generation failed:", error);
169
+ throw error;
170
+ }
171
+ };
@@ -0,0 +1,13 @@
1
+ /** @type {import('tailwindcss').Config} */
2
+ module.exports = {
3
+ content: ["./src/**/*.{js,ts,jsx,tsx}"],
4
+ darkMode: "class",
5
+ theme: {
6
+ extend: {
7
+ fontFamily: {
8
+ sans: ["Inter", "system-ui", "-apple-system", "sans-serif"],
9
+ },
10
+ },
11
+ },
12
+ plugins: [],
13
+ };
@@ -0,0 +1,17 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ESNext",
4
+ "lib": ["DOM", "DOM.Iterable", "ESNext"],
5
+ "module": "ESNext",
6
+ "moduleResolution": "Bundler",
7
+ "jsx": "react-jsx",
8
+ "strict": true,
9
+ "declaration": true,
10
+ "outDir": "dist",
11
+ "esModuleInterop": true,
12
+ "skipLibCheck": true,
13
+ "noImplicitAny": false,
14
+ "forceConsistentCasingInFileNames": true
15
+ },
16
+ "include": ["src/**/*"]
17
+ }
Binary file
Binary file
Binary file