mobigrid-module 1.0.3 → 1.0.5
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/ui/pagination.d.ts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.esm.js.map +1 -1
- package/dist/index.tsx.map +1 -1
- package/dist/vite.config.d.ts +2 -0
- package/index.tsx +254 -0
- package/package.json +3 -4
    
        package/index.tsx
    ADDED
    
    | @@ -0,0 +1,254 @@ | |
| 1 | 
            +
            import React from "react";
         | 
| 2 | 
            +
            import { useEffect, useState } from "react";
         | 
| 3 | 
            +
            import { format } from "date-fns";
         | 
| 4 | 
            +
            import axios from "axios";
         | 
| 5 | 
            +
            import { Alert, AlertTitle, AlertDescription } from "./components/ui/alert";
         | 
| 6 | 
            +
            import { AlertCircle } from "lucide-react";
         | 
| 7 | 
            +
            import { PageHeader } from "./components/Layout/PageHeader";
         | 
| 8 | 
            +
            import { CustomTable } from "./components/CustomTable/CustomTable";
         | 
| 9 | 
            +
            import Pagination from "./components/CustomTable/Pagination";
         | 
| 10 | 
            +
             | 
| 11 | 
            +
            const ITEMS_PER_PAGE = 14;
         | 
| 12 | 
            +
             | 
| 13 | 
            +
            interface ContainerProps {
         | 
| 14 | 
            +
              configUrl: string;
         | 
| 15 | 
            +
              preJsUrl: string;
         | 
| 16 | 
            +
              itemsPerPage?: number;
         | 
| 17 | 
            +
              children?: React.ReactNode;
         | 
| 18 | 
            +
            }
         | 
| 19 | 
            +
             | 
| 20 | 
            +
            const MobigridModule = ({
         | 
| 21 | 
            +
              configUrl,
         | 
| 22 | 
            +
              preJsUrl,
         | 
| 23 | 
            +
              itemsPerPage = ITEMS_PER_PAGE,
         | 
| 24 | 
            +
              children,
         | 
| 25 | 
            +
            }: ContainerProps) => {
         | 
| 26 | 
            +
              const [config, setConfig] = useState<any>(null);
         | 
| 27 | 
            +
              const [invalidConfig, setInvalidConfig] = useState(false);
         | 
| 28 | 
            +
              const [errors, setErrors] = useState<
         | 
| 29 | 
            +
                Array<{
         | 
| 30 | 
            +
                  title: string;
         | 
| 31 | 
            +
                  message: string;
         | 
| 32 | 
            +
                }>
         | 
| 33 | 
            +
              >([]);
         | 
| 34 | 
            +
              const [isLoading, setIsLoading] = useState(false);
         | 
| 35 | 
            +
              const [data, setData] = useState([]);
         | 
| 36 | 
            +
              const [count, setCount] = useState(0);
         | 
| 37 | 
            +
              const [currentPage, setCurrentPage] = useState(1);
         | 
| 38 | 
            +
              const [totalPages, setTotalPages] = useState(1);
         | 
| 39 | 
            +
              const [filters, setFilters] = useState({
         | 
| 40 | 
            +
                fromDate: new Date(
         | 
| 41 | 
            +
                  new Date().getFullYear(),
         | 
| 42 | 
            +
                  new Date().getMonth(),
         | 
| 43 | 
            +
                  1
         | 
| 44 | 
            +
                ).toLocaleString(),
         | 
| 45 | 
            +
                toDate: new Date(
         | 
| 46 | 
            +
                  new Date().getFullYear(),
         | 
| 47 | 
            +
                  new Date().getMonth() + 1,
         | 
| 48 | 
            +
                  0,
         | 
| 49 | 
            +
                  23,
         | 
| 50 | 
            +
                  59,
         | 
| 51 | 
            +
                  59
         | 
| 52 | 
            +
                ).toLocaleString(),
         | 
| 53 | 
            +
              });
         | 
| 54 | 
            +
              const [filterOptions, setFilterOptions] = useState<any[]>([]);
         | 
| 55 | 
            +
             | 
| 56 | 
            +
              useEffect(() => {
         | 
| 57 | 
            +
                const loadConfig = async () => {
         | 
| 58 | 
            +
                  try {
         | 
| 59 | 
            +
                    const response = await fetch(configUrl);
         | 
| 60 | 
            +
                    const data = await response.json();
         | 
| 61 | 
            +
                    if (data && data.title && data.data_url && data.colomns) {
         | 
| 62 | 
            +
                      setConfig(data);
         | 
| 63 | 
            +
                      setFilterOptions(data.Filters?.flat() || []);
         | 
| 64 | 
            +
                    } else {
         | 
| 65 | 
            +
                      setInvalidConfig(true);
         | 
| 66 | 
            +
                    }
         | 
| 67 | 
            +
                  } catch (error) {
         | 
| 68 | 
            +
                    setInvalidConfig(true);
         | 
| 69 | 
            +
                  }
         | 
| 70 | 
            +
                };
         | 
| 71 | 
            +
             | 
| 72 | 
            +
                const loadFunctions = async () => {
         | 
| 73 | 
            +
                  const response = await fetch(preJsUrl);
         | 
| 74 | 
            +
                  const script = document.createElement("script");
         | 
| 75 | 
            +
                  script.textContent = await response.text();
         | 
| 76 | 
            +
                  document.head.appendChild(script);
         | 
| 77 | 
            +
                };
         | 
| 78 | 
            +
             | 
| 79 | 
            +
                if (configUrl && preJsUrl) {
         | 
| 80 | 
            +
                  loadConfig();
         | 
| 81 | 
            +
                  loadFunctions();
         | 
| 82 | 
            +
                }
         | 
| 83 | 
            +
              }, [configUrl, preJsUrl]);
         | 
| 84 | 
            +
             | 
| 85 | 
            +
              useEffect(() => {
         | 
| 86 | 
            +
                const controller = new AbortController();
         | 
| 87 | 
            +
             | 
| 88 | 
            +
                const initializeFilters = async () => {
         | 
| 89 | 
            +
                  const filters = config?.Filters?.flat() || [];
         | 
| 90 | 
            +
                  for (const filter of filters) {
         | 
| 91 | 
            +
                    if (filter.type === "Select" && filter.urlSource) {
         | 
| 92 | 
            +
                      await fetchFilterOptions(filter, controller.signal);
         | 
| 93 | 
            +
                    }
         | 
| 94 | 
            +
                  }
         | 
| 95 | 
            +
                };
         | 
| 96 | 
            +
             | 
| 97 | 
            +
                if (config) initializeFilters();
         | 
| 98 | 
            +
             | 
| 99 | 
            +
                return () => controller.abort();
         | 
| 100 | 
            +
              }, [config]);
         | 
| 101 | 
            +
             | 
| 102 | 
            +
              const getData = async () => {
         | 
| 103 | 
            +
                setErrors([]);
         | 
| 104 | 
            +
                const postBody = {
         | 
| 105 | 
            +
                  page: currentPage,
         | 
| 106 | 
            +
                  filters: {
         | 
| 107 | 
            +
                    ...filters,
         | 
| 108 | 
            +
                    fromDate: format(filters.fromDate, "MM-dd-yyyy HH:mm"),
         | 
| 109 | 
            +
                    toDate: format(filters.toDate, "MM-dd-yyyy HH:mm"),
         | 
| 110 | 
            +
                  },
         | 
| 111 | 
            +
                };
         | 
| 112 | 
            +
             | 
| 113 | 
            +
                setIsLoading(true);
         | 
| 114 | 
            +
                try {
         | 
| 115 | 
            +
                  const response = await axios.post(config.data_url, postBody);
         | 
| 116 | 
            +
                  setIsLoading(false);
         | 
| 117 | 
            +
             | 
| 118 | 
            +
                  if (response?.data) {
         | 
| 119 | 
            +
                    setData(response.data?.DATA);
         | 
| 120 | 
            +
                    setCount(response.data?.PAGESNUM);
         | 
| 121 | 
            +
                    setTotalPages(Math.ceil(response.data?.PAGESNUM / itemsPerPage) || 1);
         | 
| 122 | 
            +
                  } else {
         | 
| 123 | 
            +
                    setInvalidConfig(true);
         | 
| 124 | 
            +
                  }
         | 
| 125 | 
            +
                } catch (error) {
         | 
| 126 | 
            +
                  setIsLoading(false);
         | 
| 127 | 
            +
                  setErrors((prev) => [
         | 
| 128 | 
            +
                    ...prev,
         | 
| 129 | 
            +
                    {
         | 
| 130 | 
            +
                      title: "Erreur",
         | 
| 131 | 
            +
                      message: "Une erreur est survenue",
         | 
| 132 | 
            +
                    },
         | 
| 133 | 
            +
                  ]);
         | 
| 134 | 
            +
                }
         | 
| 135 | 
            +
              };
         | 
| 136 | 
            +
             | 
| 137 | 
            +
              const fetchFilterOptions = async (filter: any, signal?: AbortSignal) => {
         | 
| 138 | 
            +
                if (filter.type === "Select" && filter.urlSource) {
         | 
| 139 | 
            +
                  try {
         | 
| 140 | 
            +
                    const response = await axios.get(filter.urlSource, { signal });
         | 
| 141 | 
            +
                    if (response?.status === 200 && response?.data) {
         | 
| 142 | 
            +
                      const options = Array.isArray(response.data) ? response.data : [];
         | 
| 143 | 
            +
                      setFilterOptions((prev) =>
         | 
| 144 | 
            +
                        prev.map((f) => (f.name === filter.name ? { ...f, options } : f))
         | 
| 145 | 
            +
                      );
         | 
| 146 | 
            +
                    }
         | 
| 147 | 
            +
                  } catch (error) {
         | 
| 148 | 
            +
                    if (!axios.isCancel(error)) {
         | 
| 149 | 
            +
                      setErrors((prev) => [
         | 
| 150 | 
            +
                        ...prev,
         | 
| 151 | 
            +
                        {
         | 
| 152 | 
            +
                          title: "Erreur",
         | 
| 153 | 
            +
                          message: `Une erreur est survenue lors de la récupération des options pour ${filter.name}`,
         | 
| 154 | 
            +
                        },
         | 
| 155 | 
            +
                      ]);
         | 
| 156 | 
            +
                      console.error(`Error fetching options for ${filter.name}:`, error);
         | 
| 157 | 
            +
                    }
         | 
| 158 | 
            +
                  }
         | 
| 159 | 
            +
                }
         | 
| 160 | 
            +
              };
         | 
| 161 | 
            +
             | 
| 162 | 
            +
              if (invalidConfig) {
         | 
| 163 | 
            +
                return (
         | 
| 164 | 
            +
                  <div
         | 
| 165 | 
            +
                    style={{
         | 
| 166 | 
            +
                      display: "flex",
         | 
| 167 | 
            +
                      justifyContent: "center",
         | 
| 168 | 
            +
                      alignItems: "center",
         | 
| 169 | 
            +
                      height: "100vh",
         | 
| 170 | 
            +
                    }}
         | 
| 171 | 
            +
                  >
         | 
| 172 | 
            +
                    <div style={{ position: "absolute" }}>
         | 
| 173 | 
            +
                      <svg
         | 
| 174 | 
            +
                        className="h-5 w-5 mx-auto mb-2"
         | 
| 175 | 
            +
                        xmlns="http://www.w3.org/2000/svg"
         | 
| 176 | 
            +
                        viewBox="0 0 24 24"
         | 
| 177 | 
            +
                        fill="red"
         | 
| 178 | 
            +
                      >
         | 
| 179 | 
            +
                        <path d="M18.3 5.71a.996.996 0 00-1.41 0L12 10.59 7.11 5.7A.996.996 0 105.7 7.11L10.59 12 5.7 16.89a.996.996 0 101.41 1.41L12 13.41l4.89 4.89a.996.996 0 101.41-1.41L13.41 12l4.89-4.89c.38-.38.38-1.02 0-1.4z" />
         | 
| 180 | 
            +
                      </svg>
         | 
| 181 | 
            +
                      <div>Invalid configuration</div>
         | 
| 182 | 
            +
                    </div>
         | 
| 183 | 
            +
                  </div>
         | 
| 184 | 
            +
                );
         | 
| 185 | 
            +
              }
         | 
| 186 | 
            +
             | 
| 187 | 
            +
              if (!config) {
         | 
| 188 | 
            +
                return (
         | 
| 189 | 
            +
                  <div
         | 
| 190 | 
            +
                    style={{
         | 
| 191 | 
            +
                      display: "flex",
         | 
| 192 | 
            +
                      justifyContent: "center",
         | 
| 193 | 
            +
                      alignItems: "center",
         | 
| 194 | 
            +
                      height: "100vh",
         | 
| 195 | 
            +
                    }}
         | 
| 196 | 
            +
                  >
         | 
| 197 | 
            +
                    <div style={{ position: "absolute" }}>
         | 
| 198 | 
            +
                      <svg
         | 
| 199 | 
            +
                        className="animate-spin h-5 w-5 mx-auto mb-2"
         | 
| 200 | 
            +
                        xmlns="http://www.w3.org/2000/svg"
         | 
| 201 | 
            +
                        fill="none"
         | 
| 202 | 
            +
                        viewBox="0 0 24 24"
         | 
| 203 | 
            +
                      >
         | 
| 204 | 
            +
                        <circle
         | 
| 205 | 
            +
                          className="opacity-25"
         | 
| 206 | 
            +
                          cx="12"
         | 
| 207 | 
            +
                          cy="12"
         | 
| 208 | 
            +
                          r="10"
         | 
| 209 | 
            +
                          stroke="currentColor"
         | 
| 210 | 
            +
                          strokeWidth="4"
         | 
| 211 | 
            +
                        ></circle>
         | 
| 212 | 
            +
                        <path
         | 
| 213 | 
            +
                          className="opacity-75"
         | 
| 214 | 
            +
                          fill="currentColor"
         | 
| 215 | 
            +
                          d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
         | 
| 216 | 
            +
                        ></path>
         | 
| 217 | 
            +
                      </svg>
         | 
| 218 | 
            +
                      <div>Loading configuration...</div>
         | 
| 219 | 
            +
                    </div>
         | 
| 220 | 
            +
                  </div>
         | 
| 221 | 
            +
                );
         | 
| 222 | 
            +
              }
         | 
| 223 | 
            +
             | 
| 224 | 
            +
              return (
         | 
| 225 | 
            +
                <div className="flex flex-col gap-4 p-4 max-w-[1300px] mx-auto mt-8 mb-">
         | 
| 226 | 
            +
                  <PageHeader
         | 
| 227 | 
            +
                    title={config.title}
         | 
| 228 | 
            +
                    configFilters={filterOptions}
         | 
| 229 | 
            +
                    filters={filters}
         | 
| 230 | 
            +
                    setFilters={setFilters}
         | 
| 231 | 
            +
                    onSearch={() => getData()}
         | 
| 232 | 
            +
                    count={count}
         | 
| 233 | 
            +
                    isLoading={isLoading}
         | 
| 234 | 
            +
                    setCurrentPage={setCurrentPage}
         | 
| 235 | 
            +
                  />
         | 
| 236 | 
            +
                  {errors.map((error, index) => (
         | 
| 237 | 
            +
                    <Alert key={index} variant="destructive">
         | 
| 238 | 
            +
                      <AlertCircle className="h-4 w-4" />
         | 
| 239 | 
            +
                      <AlertTitle>{error.title}</AlertTitle>
         | 
| 240 | 
            +
                      <AlertDescription>{error.message}</AlertDescription>
         | 
| 241 | 
            +
                    </Alert>
         | 
| 242 | 
            +
                  ))}
         | 
| 243 | 
            +
                  <CustomTable data={data} columns={config.colomns} isLoading={isLoading} />
         | 
| 244 | 
            +
                  <Pagination
         | 
| 245 | 
            +
                    currentPage={currentPage}
         | 
| 246 | 
            +
                    totalPages={totalPages}
         | 
| 247 | 
            +
                    onPageChange={setCurrentPage}
         | 
| 248 | 
            +
                  />
         | 
| 249 | 
            +
                  {children}
         | 
| 250 | 
            +
                </div>
         | 
| 251 | 
            +
              );
         | 
| 252 | 
            +
            }
         | 
| 253 | 
            +
             | 
| 254 | 
            +
            export {MobigridModule as default};
         | 
    
        package/package.json
    CHANGED
    
    | @@ -1,6 +1,6 @@ | |
| 1 1 | 
             
            {
         | 
| 2 2 | 
             
              "name": "mobigrid-module",
         | 
| 3 | 
            -
              "version": "1.0. | 
| 3 | 
            +
              "version": "1.0.5",
         | 
| 4 4 | 
             
              "description": "",
         | 
| 5 5 | 
             
              "main": "index.tsx",
         | 
| 6 6 | 
             
              "type": "module",
         | 
| @@ -64,9 +64,8 @@ | |
| 64 64 | 
             
                "typescript-eslint": "^8.11.0",
         | 
| 65 65 | 
             
                "vite": "^5.4.10"
         | 
| 66 66 | 
             
              },
         | 
| 67 | 
            -
              "types": "dist/ | 
| 67 | 
            +
              "types": "dist/index.d.ts",
         | 
| 68 68 | 
             
              "files": [
         | 
| 69 | 
            -
                "dist" | 
| 70 | 
            -
                "dist/types"
         | 
| 69 | 
            +
                "dist"
         | 
| 71 70 | 
             
              ]
         | 
| 72 71 | 
             
            }
         |