diff --git a/package-lock.json b/package-lock.json index cf833a5..4dee62a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -42,6 +42,7 @@ "react-window": "^1.8.10", "reactstrap": "^9.2.3", "sass": "^1.79.4", + "vite-plugin-env-compatible": "^2.0.1", "yup": "^1.4.0", "zustand": "^4.5.5" }, @@ -5553,38 +5554,6 @@ "csstype": "^3.0.2" } }, - "node_modules/dotenv": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.2.0.tgz", - "integrity": "sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=8" - } - }, - "node_modules/dotenv-expand": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-5.1.0.tgz", - "integrity": "sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==", - "dev": true, - "license": "BSD-2-Clause" - }, - "node_modules/dunder-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", - "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "es-errors": "^1.3.0", - "gopd": "^1.2.0" - }, - "engines": { - "node": ">= 0.4" - } - }, "node_modules/ejs": { "version": "3.1.10", "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", @@ -16249,48 +16218,6 @@ "vite": ">=2.0.0" } }, - "node_modules/vite-plugin-env-compatible": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/vite-plugin-env-compatible/-/vite-plugin-env-compatible-2.0.1.tgz", - "integrity": "sha512-DRrOZTg/W44ojVQQfGSMPEgYQGzp5TeIpt9cpaK35hTOC/b2D7Ffl8/RIgK8vQ0mlnDIUgETcA173bnMEkyzdw==", - "dev": true, - "license": "MIT", - "dependencies": { - "dotenv": "8.2.0", - "dotenv-expand": "5.1.0" - } - }, - "node_modules/vite-plugin-pwa": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/vite-plugin-pwa/-/vite-plugin-pwa-1.0.3.tgz", - "integrity": "sha512-/OpqIpUldALGxcsEnv/ekQiQ5xHkQ53wcoN5ewX4jiIDNGs3W+eNcI1WYZeyOLmzoEjg09D7aX0O89YGjen1aw==", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "^4.3.6", - "pretty-bytes": "^6.1.1", - "tinyglobby": "^0.2.10", - "workbox-build": "^7.3.0", - "workbox-window": "^7.3.0" - }, - "engines": { - "node": ">=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/antfu" - }, - "peerDependencies": { - "@vite-pwa/assets-generator": "^1.0.0", - "vite": "^3.1.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0", - "workbox-build": "^7.3.0", - "workbox-window": "^7.3.0" - }, - "peerDependenciesMeta": { - "@vite-pwa/assets-generator": { - "optional": true - } - } - }, "node_modules/void-elements": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz", @@ -21135,29 +21062,6 @@ "csstype": "^3.0.2" } }, - "dotenv": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.2.0.tgz", - "integrity": "sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw==", - "dev": true - }, - "dotenv-expand": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-5.1.0.tgz", - "integrity": "sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==", - "dev": true - }, - "dunder-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", - "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "dev": true, - "requires": { - "call-bind-apply-helpers": "^1.0.1", - "es-errors": "^1.3.0", - "gopd": "^1.2.0" - } - }, "ejs": { "version": "3.1.10", "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", @@ -28489,29 +28393,6 @@ "fs-extra": "^10.0.0" } }, - "vite-plugin-env-compatible": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/vite-plugin-env-compatible/-/vite-plugin-env-compatible-2.0.1.tgz", - "integrity": "sha512-DRrOZTg/W44ojVQQfGSMPEgYQGzp5TeIpt9cpaK35hTOC/b2D7Ffl8/RIgK8vQ0mlnDIUgETcA173bnMEkyzdw==", - "dev": true, - "requires": { - "dotenv": "8.2.0", - "dotenv-expand": "5.1.0" - } - }, - "vite-plugin-pwa": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/vite-plugin-pwa/-/vite-plugin-pwa-1.0.3.tgz", - "integrity": "sha512-/OpqIpUldALGxcsEnv/ekQiQ5xHkQ53wcoN5ewX4jiIDNGs3W+eNcI1WYZeyOLmzoEjg09D7aX0O89YGjen1aw==", - "dev": true, - "requires": { - "debug": "^4.3.6", - "pretty-bytes": "^6.1.1", - "tinyglobby": "^0.2.10", - "workbox-build": "^7.3.0", - "workbox-window": "^7.3.0" - } - }, "void-elements": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz", diff --git a/package.json b/package.json index 050b5f8..81cd56d 100644 --- a/package.json +++ b/package.json @@ -37,6 +37,7 @@ "react-window": "^1.8.10", "reactstrap": "^9.2.3", "sass": "^1.79.4", + "vite-plugin-env-compatible": "^2.0.1", "yup": "^1.4.0", "zustand": "^4.5.5" }, @@ -46,7 +47,12 @@ "test": "vite jest", "preview": "vite preview --outDir build --port 4173", "eject": "react-scripts eject", - "format": "prettier --write ." + "format": "prettier --write .", + "g:api": "node src/Extensions/FileGenerator/generateApi.js", + "g:column": "node src/Extensions/FileGenerator/generateColumn.js", + "g:formutil": "node src/Extensions/FileGenerator/generateformUtils.js", + "g:page": "node src/Extensions/FileGenerator/generatePage.js", + "g:dashboard": "node src/Extensions/FileGenerator/generateDashboard.js " }, "eslintConfig": { "extends": [ diff --git a/src/Extensions/FileGenerator/generateAbility.js b/src/Extensions/FileGenerator/generateAbility.js new file mode 100644 index 0000000..b29ca13 --- /dev/null +++ b/src/Extensions/FileGenerator/generateAbility.js @@ -0,0 +1,84 @@ +const fs = require("fs"); +const path = require("path"); + +// File path where the abilities are stored +const ABILITIES_FILE = path.join(__dirname, "./src/utils/hasAbilityFn.ts"); + +// Function to normalize the input name to UPPER_SNAKE_CASE (e.g., test_moaz => TEST_MOAZ, TestMoaz => TEST_MOAZ) +const normalizeToUpperSnakeCase = (input) => { + return input + .replace(/([a-z])([A-Z])/g, "$1_$2") // camelCase to snake_case + .replace(/[_\s]+/g, "_") // normalize multiple underscores or spaces + .toUpperCase(); // Convert to uppercase +}; + +// Function to capitalize the first letter of a word +const capitalizeFirstLetter = (word) => word.charAt(0).toUpperCase() + word.slice(1); + +// Take the name dynamically from the terminal +const nameInput = process.argv[2]; + +if (!nameInput) { + console.error("Please provide a name as an argument."); + process.exit(1); +} + +// Normalize input name (handle camelCase, snake_case, PascalCase) +const normalizedInput = normalizeToUpperSnakeCase(nameInput); + +// Generate ability functions for the given name +const generateAbilityFunctions = (name) => { + const capitalized = capitalizeFirstLetter(name.toLowerCase()); + return ` +export const canAdd${capitalized} = hasAbility( + ABILITIES_ENUM.${name}, + ABILITIES_VALUES_ENUM.STORE, +); +export const canEdit${capitalized} = hasAbility( + ABILITIES_ENUM.${name}, + ABILITIES_VALUES_ENUM.UPDATE, +); +export const canDelete${capitalized} = hasAbility( + ABILITIES_ENUM.${name}, + ABILITIES_VALUES_ENUM.DELETE, +); +export const canShow${capitalized} = hasAbility( + ABILITIES_ENUM.${name}, + ABILITIES_VALUES_ENUM.SHOW, +); +export const canIndex${capitalized} = hasAbility( + ABILITIES_ENUM.${name}, + ABILITIES_VALUES_ENUM.INDEX, +); +`.trim(); +}; + +const updateAbilitiesFile = () => { + if (!fs.existsSync(ABILITIES_FILE)) { + console.error(`Error: File ${ABILITIES_FILE} does not exist.`); + return; + } + + // Read the existing content of the file + const fileContent = fs.readFileSync(ABILITIES_FILE, "utf-8"); + + // Check for duplicates + const alreadyExists = fileContent.includes(`canAdd${capitalizeFirstLetter(normalizedInput)}`); + if (alreadyExists) { + console.log(`Abilities for "${normalizedInput}" already exist. No changes made.`); + return; + } + + // Generate new abilities + const newAbilities = generateAbilityFunctions(normalizedInput); + + // Append the new abilities at the end of the file + const updatedContent = `${fileContent.trim()}\n\n${newAbilities}\n`; + + // Write the updated content back to the file + fs.writeFileSync(ABILITIES_FILE, updatedContent, "utf-8"); + console.log(`Abilities for "${normalizedInput}" added successfully to hasAbilityFn.ts.`); +}; + +// Run the update +updateAbilitiesFile(); diff --git a/src/Extensions/FileGenerator/generateAddModal.js b/src/Extensions/FileGenerator/generateAddModal.js new file mode 100644 index 0000000..566b27f --- /dev/null +++ b/src/Extensions/FileGenerator/generateAddModal.js @@ -0,0 +1,68 @@ +const fs = require('fs'); + +// Get the file name from the command line arguments +const fileName = process.argv[2]; + +// Check if a file name is provided +if (!fileName) { + console.error('Please provide a file name.'); + process.exit(1); +} + + + +let FileContainer = ` + +import React from "react"; +import { getInitialValues, getValidationSchema } from "./FormUtil"; +import { ModalEnum } from "../../../enums/Model"; +import LayoutModel from "../../../Layout/Dashboard/LayoutModel"; +import { QueryStatusEnum } from "../../../enums/QueryStatus"; +import ModelForm from "./ModelForm"; +import { useAdd${capitalizeFirstLetter(fileName)} } from "../../../api/${fileName}"; + +const AddModel: React.FC = () => { + const { mutate, status } = useAdd${capitalizeFirstLetter(fileName)}(); + + const handleSubmit = (values: any) => { + mutate({ + ...values, + }); + }; + return ( + <> + + + + + ); +}; + +export default AddModel; + + +` +fs.writeFileSync('src/Pages/'+fileName+"/Model"+"/"+"AddModel.tsx", +FileContainer +); + +console.log(`File "${fileName}" generated successfully.`); + + + + + +function capitalizeFirstLetter(word) { + return (word).charAt(0).toUpperCase() + (word).slice(1); +} + +function capitalizeAllWord(word) { + return (word).toUpperCase() ; +} \ No newline at end of file diff --git a/src/Extensions/FileGenerator/generateApi.js b/src/Extensions/FileGenerator/generateApi.js new file mode 100644 index 0000000..aecb13c --- /dev/null +++ b/src/Extensions/FileGenerator/generateApi.js @@ -0,0 +1,45 @@ +const fs = require('fs'); + +const fileName = process.argv[2] + + +if (!fileName) { + console.error('Please provide a file name.'); + process.exit(1); +} + + +let FileContainer = ` + +import useAddMutation from "./helper/useAddMutation"; +import useDeleteMutation from "./helper/useDeleteMutation"; +import useGetQuery from "./helper/useGetQuery"; +import useUpdateMutation from "./helper/useUpdateMutation"; + +const API = { + GET: "/${fileName}", + ADD: "/${fileName}", + DELETE: "/${fileName}", + UPDATE: "/${fileName}", +}; + +const KEY = "${fileName}"; + +export const useGetAll${capitalizeFirstLetter(fileName)} = (params?: any, options?: any) => + useGetQuery(KEY, API.GET, params, options); +export const useAdd${capitalizeFirstLetter(fileName)} = () => useAddMutation(KEY, API.ADD); +export const useUpdate${capitalizeFirstLetter(fileName)} = (params?: any) => useUpdateMutation(KEY, API.UPDATE); +export const useDelete${capitalizeFirstLetter(fileName)} = (params?: any) => + useDeleteMutation(KEY, API.DELETE); + +` +fs.writeFileSync('src/api/' + fileName + ".ts", + FileContainer +); + +console.log(`File "${fileName}" generated successfully.`); + + +function capitalizeFirstLetter(word) { + return (word).charAt(0).toUpperCase() + (word).slice(1); +} \ No newline at end of file diff --git a/src/Extensions/FileGenerator/generateColumn.js b/src/Extensions/FileGenerator/generateColumn.js new file mode 100644 index 0000000..279bb66 --- /dev/null +++ b/src/Extensions/FileGenerator/generateColumn.js @@ -0,0 +1,122 @@ +const fs = require('fs'); + +// Get the file name from the command line arguments +const fileName = process.argv[2]; + +// Check if a file name is provided +if (!fileName) { + console.error('Please provide a file name.'); + process.exit(1); +} + + + +let FileContainer = ` + +import { TableColumnsType } from "antd"; +import useModalHandler from "../../utils/useModalHandler"; +import { ModalEnum } from "../../enums/Model"; +import { useObjectToEdit } from "../../zustand/ObjectToEditState"; +import { useTranslation } from "react-i18next"; +import { useNavigate } from "react-router-dom"; +import { + canDelete${capitalizeFirstLetter(fileName)}, + canEdit${capitalizeFirstLetter(fileName)}, + canShow${capitalizeFirstLetter(fileName)}, +} from "../../utils/hasAbilityFn"; +import ActionButtons from "../../Components/Table/ActionButtons"; +import ColumnsImage from "../../Components/Columns/ColumnsImage"; +import { ${capitalizeFirstLetter(fileName)} } from "../../types/${capitalizeFirstLetter(fileName)}"; +import { useFilterStateState } from "../../zustand/Filter"; + +export const useColumns = () => { + const { handel_open_model } = useModalHandler(); + + const { setObjectToEdit } = useObjectToEdit((state) => state); + const navigate = useNavigate(); + const { setFilter } = useFilterStateState(); + + const handelShow = (record: ${capitalizeFirstLetter(fileName)}) => { + setFilter({}); + navigate(record?.id); + }; + + const handelDelete = (data: ${capitalizeFirstLetter(fileName)}) => { + setObjectToEdit(data); + handel_open_model(ModalEnum?.${capitalizeAllWord(fileName)}_DELETE); + }; + + const handleEdit = (record: ${capitalizeFirstLetter(fileName)}) => { + setObjectToEdit(record); + handel_open_model(ModalEnum?.${capitalizeAllWord(fileName)}_EDIT); + }; + const [t] = useTranslation(); + + const columns: TableColumnsType<${capitalizeFirstLetter(fileName)}> = [ + { + title: t("columns.id"), + dataIndex: "id", + key: "id", + align: "center", + render: (_text, record) => record?.id, + }, + { + title: ("columns.name"), + dataIndex: "name", + key: "name", + align: "center", + render: (_text, record) => record?.name, + ellipsis: true, + }, + { + title: t("columns.image"), + dataIndex: "image", + key: "image", + align: "center", + render: (_text: any, record: ${capitalizeFirstLetter(fileName)}) => { + let str = record?.image; + + return ; + }, + }, + { + title: t("columns.procedure"), + key: "actions", + align: "center", + width: "25vw", + render: (_text, record, index) => { + return ( + handelDelete(record)} + onEdit={() => handleEdit(record)} + onShow={() => handelShow(record)} + /> + ); + }, + }, + ]; + + return columns; +}; + + +` +fs.writeFileSync('src/Pages/'+fileName+"/"+"useTableColumns.tsx", +FileContainer +); + +console.log(`File "${fileName}" generated successfully.`); + + + +function capitalizeFirstLetter(word) { + return (word).charAt(0).toUpperCase() + (word).slice(1); +} + +function capitalizeAllWord(word) { +return (word).toUpperCase() ; +} \ No newline at end of file diff --git a/src/Extensions/FileGenerator/generateDashboard.js b/src/Extensions/FileGenerator/generateDashboard.js new file mode 100644 index 0000000..5b514a1 --- /dev/null +++ b/src/Extensions/FileGenerator/generateDashboard.js @@ -0,0 +1,39 @@ +const { execSync } = require('child_process'); + +const fileName = process.argv[2]; + +// Define commands +const commands = [ + `node src/Extensions/FileGenerator/generateApi.js ${fileName}`, + `node src/Extensions/FileGenerator/generatePage.js ${fileName}`, + `node src/Extensions/FileGenerator/generateTable.js ${fileName}`, + `node src/Extensions/FileGenerator/generateColumn.js ${fileName}`, + `node src/Extensions/FileGenerator/generateIndex.js ${fileName}`, + `node src/Extensions/FileGenerator/generateFormUtils.js ${fileName}`, + `node src/Extensions/FileGenerator/generateAddModal.js ${fileName}`, + `node src/Extensions/FileGenerator/generateEditModal.js ${fileName}`, + `node src/Extensions/FileGenerator/generateModelForm.js ${fileName}`, + `node src/Extensions/FileGenerator/generateFilterForm.js ${fileName}`, + `node src/Extensions/FileGenerator/generateType.js ${fileName}`, + `node src/Extensions/FileGenerator/generateParamsEnum.js ${fileName}`, + // `node src/Extensions/FileGenerator/generateRoute.js ${fileName}`, + // `node src/Extensions/FileGenerator/generateModelEnum.js ${fileName}`, + // `node src/Extensions/FileGenerator/generateAbility.js ${fileName}` + +]; + +// Execute each command sequentially +const runCommands = () => { + for (const command of commands) { + try { + console.log(`Running: ${command}`); + execSync(command, { stdio: 'inherit' }); + console.log(`${command} executed successfully.\n`); + } catch (error) { + console.error(`Error executing ${command}:`, error); + process.exit(1); // Exit if any command fails + } + } +}; + +runCommands(); diff --git a/src/Extensions/FileGenerator/generateEditModal.js b/src/Extensions/FileGenerator/generateEditModal.js new file mode 100644 index 0000000..641c227 --- /dev/null +++ b/src/Extensions/FileGenerator/generateEditModal.js @@ -0,0 +1,67 @@ +const fs = require('fs'); + +// Get the file name from the command line arguments +const fileName = process.argv[2]; + +// Check if a file name is provided +if (!fileName) { + console.error('Please provide a file name.'); + process.exit(1); +} + +let FileContainer = ` + +import React from "react"; +import { getInitialValues, getValidationSchema } from "./FormUtil"; +import { ModalEnum } from "../../../enums/Model"; +import LayoutModel from "../../../Layout/Dashboard/LayoutModel"; +import ModelForm from "./ModelForm"; +import { QueryStatusEnum } from "../../../enums/QueryStatus"; +import { useObjectToEdit } from "../../../zustand/ObjectToEditState"; +import { useUpdate${capitalizeFirstLetter(fileName)} } from "../../../api/${fileName}"; +import { handelImageState } from "../../../utils/DataToSendImageState"; + +const EditModel: React.FC = () => { + const { mutate, status } = useUpdate${capitalizeFirstLetter(fileName)}(); + const { objectToEdit } = useObjectToEdit((state) => state); + + const handleSubmit = (values: any) => { + const Data_to_send = { ...values }; + const handelImage = handelImageState(Data_to_send, "icon"); + mutate(handelImage); + }; + + return ( + <> + + + + + ); +}; + +export default EditModel; + +` +fs.writeFileSync('src/Pages/'+fileName+"/Model"+"/"+"EditModel.tsx", +FileContainer +); + +console.log(`File "${fileName}" generated successfully.`); + + +function capitalizeFirstLetter(word) { + return (word).charAt(0).toUpperCase() + (word).slice(1); +} + +function capitalizeAllWord(word) { + return (word).toUpperCase() ; +} \ No newline at end of file diff --git a/src/Extensions/FileGenerator/generateFilterForm.js b/src/Extensions/FileGenerator/generateFilterForm.js new file mode 100644 index 0000000..e65a716 --- /dev/null +++ b/src/Extensions/FileGenerator/generateFilterForm.js @@ -0,0 +1,47 @@ +const fs = require('fs'); + +// Get the file name from the command line arguments +const fileName = process.argv[2]; + +// Check if a file name is provided +if (!fileName) { + console.error('Please provide a file name.'); + process.exit(1); +} + +let FileContainer = ` +import React from "react"; +import ValidationField from "../../../Components/ValidationField/ValidationField"; +import { Col, Row } from "reactstrap"; +import { useFormikContext } from "formik"; + +const FilterForm = () => { + const formik = useFormikContext(); + + return ( +
+ + + + + +
+ ); +}; + +export default FilterForm; + +` +fs.writeFileSync('src/Pages/' + fileName +"/Model"+"/"+"FilterForm.tsx", + FileContainer +); + +console.log(`File "${fileName}" generated successfully.`); + + + + + +function capitalizeFirstLetter(word) { + return (word).charAt(0).toUpperCase() + (word).slice(1); +} \ No newline at end of file diff --git a/src/Extensions/FileGenerator/generateFormUtils.js b/src/Extensions/FileGenerator/generateFormUtils.js new file mode 100644 index 0000000..8ba2af6 --- /dev/null +++ b/src/Extensions/FileGenerator/generateFormUtils.js @@ -0,0 +1,51 @@ +const fs = require('fs'); +const path = require('path'); + +// Get the file name from the command line arguments +const fileName = process.argv[2]; + +// Check if a file name is provided +if (!fileName) { + console.error('Please provide a file name.'); + process.exit(1); +} + +const folderPath = path.join('src/Pages', fileName, 'Model'); + +// Ensure the directory exists (creates the folder if missing) +fs.mkdirSync(folderPath, { recursive: true }); + + +let FileContainer = ` + +import * as Yup from "yup"; +import { ${capitalizeFirstLetter(fileName)}, ${capitalizeFirstLetter(fileName)}InitialValues } from "../../../types/${capitalizeFirstLetter(fileName)}"; + +export const getInitialValues = ( + objectToEdit: Partial<${capitalizeFirstLetter(fileName)}>, +): ${capitalizeFirstLetter(fileName)}InitialValues => { + + return { + id: objectToEdit?.id, + name: objectToEdit?.name ?? "", + }; +}; + +export const getValidationSchema = () => { + // validate input + return Yup.object().shape({ + name: Yup.string().required("validation.required"), + }); +}; + +` + +fs.writeFileSync(path.join(folderPath, 'FormUtil.ts'), FileContainer); + + +console.log(`File "${fileName}" generated successfully.`); + + +function capitalizeFirstLetter(word) { + return (word).charAt(0).toUpperCase() + (word).slice(1); +} \ No newline at end of file diff --git a/src/Extensions/FileGenerator/generateIndex.js b/src/Extensions/FileGenerator/generateIndex.js new file mode 100644 index 0000000..d37e917 --- /dev/null +++ b/src/Extensions/FileGenerator/generateIndex.js @@ -0,0 +1,44 @@ +const fs = require('fs'); + +// Get the file name from the command line arguments +const fileName = process.argv[2]; + +// Check if a file name is provided +if (!fileName) { + console.error('Please provide a file name.'); + process.exit(1); +} + +const folderPath = 'src/Pages/'+fileName; + + +if (!fs.existsSync(folderPath)) { + fs.mkdirSync(folderPath, { recursive: true }); +} + +let FileContainer = ` +import { useColumns } from "./useTableColumns"; +import Table from "./Table"; +import AddModalForm from "./Model/AddModel"; +import EditModalForm from "./Model/EditModel"; + +export { + Table, + useColumns, + AddModalForm, + EditModalForm, +}; + + +` + +fs.writeFileSync('src/Pages/'+fileName+"/"+"index.tsx", +FileContainer +); + +console.log(`File "${fileName}" generated successfully.`); + + +function capitalizeFirstLetter(word) { + return (word).charAt(0).toUpperCase() + (word).slice(1); +} \ No newline at end of file diff --git a/src/Extensions/FileGenerator/generateModelEnum.js b/src/Extensions/FileGenerator/generateModelEnum.js new file mode 100644 index 0000000..eeb9b98 --- /dev/null +++ b/src/Extensions/FileGenerator/generateModelEnum.js @@ -0,0 +1,73 @@ +const fs = require("fs"); +const path = require("path"); + +// Path to the ModalEnum file +const MODEL_ENUM_FILE = "./src/enums/Model.ts"; + +// Function to convert input to UPPER_SNAKE_CASE +const toUpperSnakeCase = (input) => { + return input + .replace(/([a-z])([A-Z])/g, "$1_$2") // Convert camelCase to snake_case + .replace(/[_\s]+/g, "_") // Normalize underscores or spaces + .toUpperCase(); // Convert to uppercase +}; + +// Take the name dynamically from the terminal +const nameInput = process.argv[2]; + +if (!nameInput) { + console.error("Please provide a name as an argument."); + process.exit(1); +} + +// Generate the enum entries for the given name +const generateEnumEntries = (name) => { + const baseName = toUpperSnakeCase(name); + return [ + ` ${baseName}_EDIT = "${baseName.toLowerCase()}.edit",`, + ` ${baseName}_ADD = "${baseName.toLowerCase()}.add",`, + ` ${baseName}_DELETE = "${baseName.toLowerCase()}.delete",` + ].join("\n"); +}; + +// Update the ModalEnum file +const updateModelEnumFile = () => { + if (!fs.existsSync(MODEL_ENUM_FILE)) { + console.error(`Error: File ${MODEL_ENUM_FILE} does not exist.`); + return; + } + + // Read the existing content of the file + const fileContent = fs.readFileSync(MODEL_ENUM_FILE, "utf-8"); + + // Generate the new entries for the given name + const newEntries = generateEnumEntries(nameInput); + + // Check if any of the new keys already exist in the file + const existingKeys = Array.from( + fileContent.matchAll(/(\w+)\s*=\s*".+?"/g), + (match) => match[1] + ); + const baseName = toUpperSnakeCase(nameInput); + + const duplicateKeys = existingKeys.filter((key) => + [`${baseName}_EDIT`, `${baseName}_ADD`, `${baseName}_DELETE`].includes(key) + ); + + if (duplicateKeys.length > 0) { + console.log(`The keys ${duplicateKeys.join(", ")} already exist. No changes made.`); + return; + } + + // Inject the new entries before the closing brace of the enum + const updatedContent = fileContent.replace( + /(export enum ModalEnum {\n)/, + `$1${newEntries}\n` + ); + + // Write the updated content back to the file + fs.writeFileSync(MODEL_ENUM_FILE, updatedContent, "utf-8"); + console.log(`Enum entries for "${nameInput}" added successfully to ModalEnum.`); +}; + +updateModelEnumFile(); diff --git a/src/Extensions/FileGenerator/generateModelForm.js b/src/Extensions/FileGenerator/generateModelForm.js new file mode 100644 index 0000000..ffc03bf --- /dev/null +++ b/src/Extensions/FileGenerator/generateModelForm.js @@ -0,0 +1,38 @@ +const fs = require('fs'); + +// Get the file name from the command line arguments +const fileName = process.argv[2]; + +// Check if a file name is provided +if (!fileName) { + console.error('Please provide a file name.'); + process.exit(1); +} + +let FileContainer = ` + +import { Col, Row } from "reactstrap"; +import ValidationField from "../../../Components/ValidationField/ValidationField"; + +const ModelForm = () => { + return ( + + + + + + ); +}; + +export default ModelForm; + + + +` +fs.writeFileSync('src/Pages/' + fileName +"/Model"+"/"+"ModelForm.tsx", + FileContainer +); + +console.log(`File "${fileName}" generated successfully.`); + + diff --git a/src/Extensions/FileGenerator/generatePage.js b/src/Extensions/FileGenerator/generatePage.js new file mode 100644 index 0000000..55d8248 --- /dev/null +++ b/src/Extensions/FileGenerator/generatePage.js @@ -0,0 +1,90 @@ +const fs = require('fs'); + +// Get the file name from the command line arguments +const fileName = process.argv[2]; + +// Check if a file name is provided +if (!fileName) { + console.error('Please provide a file name.'); + process.exit(1); +} + +const folderPath = 'src/Pages/'+fileName; + + +if (!fs.existsSync(folderPath)) { + fs.mkdirSync(folderPath, { recursive: true }); +} + + +let FileContainer = ` + +import { useTranslation } from "react-i18next"; +import { lazy, Suspense } from "react"; +import { Spin } from "antd"; +import useSetPageTitle from "../../Hooks/useSetPageTitle"; +import { ModalEnum } from "../../enums/Model"; +import { useDelete${capitalizeFirstLetter(fileName)} } from "../../api/${fileName}"; +import PageHeader from "../../Layout/Dashboard/PageHeader"; +import FilterLayout from "../../Layout/Dashboard/FilterLayout"; +import FilterForm from "./Model/FilterForm"; +import { canAdd${capitalizeFirstLetter(fileName)} } from "../../utils/hasAbilityFn"; + +const Table = lazy(() => import("./Table")); +const AddModalForm = lazy(() => import("./Model/AddModel")); +const EditModalForm = lazy(() => import("./Model/EditModel")); +const DeleteModalForm = lazy( + () => import("../../Layout/Dashboard/DeleteModels"), +); + +const TableHeader = () => { + const [t] = useTranslation(); + const deleteMutation = useDelete${capitalizeFirstLetter(fileName)}(); + + useSetPageTitle([ + { name: t('page_header.home'), path: "/" }, + { name: t('page_header.${fileName}'), path: "${fileName}" }, + ]); + + return ( +
+ }> + + } + filterTitle="table.${fileName}" + /> + + + + + + + ); +}; + +export default TableHeader; + +` + +fs.writeFileSync('src/Pages/'+fileName+"/"+"Page.tsx", +FileContainer +); + +console.log(`File "${fileName}" generated successfully.`); + + +function capitalizeFirstLetter(word) { + return (word).charAt(0).toUpperCase() + (word).slice(1); +} + +function capitalizeAllWord(word) { + return (word).toUpperCase() ; +} \ No newline at end of file diff --git a/src/Extensions/FileGenerator/generateParamsEnum.js b/src/Extensions/FileGenerator/generateParamsEnum.js new file mode 100644 index 0000000..cb1dc3b --- /dev/null +++ b/src/Extensions/FileGenerator/generateParamsEnum.js @@ -0,0 +1,39 @@ +const fs = require('fs'); +const path = require('path'); + +// Function to capitalize and add "_ID" +function capitalizeAllWord(word) { + return word.toUpperCase() + "_ID"; +} + +const fileName = process.argv[2]; + +if (!fileName) { + console.error('Please provide a file name.'); + process.exit(1); +} + +// Define the enum entry to add +const enumKey = capitalizeAllWord(fileName); +const enumValue = `${fileName.toLowerCase()}_id`; +const newEntry = ` ${enumKey} = "${enumValue}",\n`; + +// Path to the params.ts file in `src/enums/` +const paramsPath = path.join(__dirname, '../../enums/params.ts'); + +try { + // Read the current contents of params.ts + const fileContents = fs.readFileSync(paramsPath, 'utf-8'); + + // Insert the new entry before the closing brace of the enum + const updatedContents = fileContents.replace( + /(export enum ParamsEnum {\n)/, + `$1${newEntry}` + ); + + // Write the updated contents back to params.ts + fs.writeFileSync(paramsPath, updatedContents); + console.log(`Enum entry "${enumKey}" added successfully to params.ts.`); +} catch (error) { + console.error(`Error updating params.ts: ${error}`); +} diff --git a/src/Extensions/FileGenerator/generateRoute.js b/src/Extensions/FileGenerator/generateRoute.js new file mode 100644 index 0000000..d5514da --- /dev/null +++ b/src/Extensions/FileGenerator/generateRoute.js @@ -0,0 +1,83 @@ +const fs = require("fs"); +const path = require("path"); + +const ROUTES_FILE = "./src/Routes.tsx"; // Path to the file that contains the route definitions +const PAGES_FOLDER = "./src/Pages"; // Folder containing the pages + +// Helper to convert a name to PascalCase +const toPascalCase = (str) => + str.replace(/(^\w|_\w)/g, (match) => match.replace("_", "").toUpperCase()); + +// Helper to convert a name to lowercase snake_case +const toSnakeCase = (str) => + str.replace(/[A-Z]/g, (letter, idx) => (idx ? "_" : "") + letter.toLowerCase()); + +// Helper to dynamically create imports and route items +const generateRoutes = () => { + // Scan the pages folder for directories + const pageDirs = fs + .readdirSync(PAGES_FOLDER, { withFileTypes: true }) + .filter((dirent) => dirent.isDirectory()) + .map((dirent) => dirent.name.toLowerCase()); // Normalize to lowercase + + // Use a Set to ensure no duplicates + const uniqueDirs = Array.from(new Set(pageDirs)); + + const imports = []; + const routes = []; + + uniqueDirs.forEach((page) => { + const componentName = toPascalCase(page); + const importPath = `./Pages/${page}/Page`; + const abilityEnum = toSnakeCase(page).toUpperCase(); + + // Add to imports + imports.push( + `const ${componentName} = React.lazy(() => import("${importPath}"));` + ); + + // Add to routes + routes.push(` + { + header: "page_header.${toSnakeCase(page)}", + element: <${componentName} />, + icon: , // Replace with actual icon + text: "sidebar.${toSnakeCase(page)}", + path: \`/\${ABILITIES_ENUM?.${abilityEnum}}\`, + abilities: ABILITIES_ENUM?.${abilityEnum}, + abilities_value: ABILITIES_VALUES_ENUM.INDEX, + prevPath: 0, + }, + `); + }); + + return { imports, routes }; +}; + +const updateRoutesFile = () => { + const { imports, routes } = generateRoutes(); + + // Remove duplicates from imports and routes + const uniqueImports = [...new Set(imports)]; + const uniqueRoutes = [...new Set(routes)]; + + // Read the existing file + const fileContent = fs.readFileSync(ROUTES_FILE, "utf-8"); + + // Replace the imports section + const updatedImports = fileContent.replace( + /\/\/ START: DYNAMIC IMPORTS[\s\S]*?\/\/ END: DYNAMIC IMPORTS/, + `// START: DYNAMIC IMPORTS\n${uniqueImports.join("\n")}\n// END: DYNAMIC IMPORTS` + ); + + // Replace the routes section + const updatedRoutes = updatedImports.replace( + /\/\/ START: DYNAMIC ROUTES[\s\S]*?\/\/ END: DYNAMIC ROUTES/, + `// START: DYNAMIC ROUTES\n${uniqueRoutes.join("\n")}\n// END: DYNAMIC ROUTES` + ); + + // Write the updated content back + fs.writeFileSync(ROUTES_FILE, updatedRoutes, "utf-8"); +}; + +updateRoutesFile(); diff --git a/src/Extensions/FileGenerator/generateTable.js b/src/Extensions/FileGenerator/generateTable.js new file mode 100644 index 0000000..7df9a70 --- /dev/null +++ b/src/Extensions/FileGenerator/generateTable.js @@ -0,0 +1,58 @@ +const fs = require('fs'); + +// Get the file name from the command line arguments +const fileName = process.argv[2]; + +// Check if a file name is provided +if (!fileName) { + console.error('Please provide a file name.'); + process.exit(1); +} + +const folderPath = 'src/Pages/'+fileName; + + +if (!fs.existsSync(folderPath)) { + fs.mkdirSync(folderPath, { recursive: true }); +} + +let FileContainer = ` + + +import { useColumns } from "./useTableColumns"; +import React from "react"; +import DataTable from "../../Layout/Dashboard/Table/DataTable"; +import { useGetAll${capitalizeFirstLetter(fileName)} } from "../../api/${fileName}"; +import { useFilterState } from "../../Components/Utils/Filter/FilterState"; +import { useFilterStateState } from "../../zustand/Filter"; + +const App: React.FC = () => { + const { filterState } = useFilterState(); + const { Filter } = useFilterStateState(); + const name = Filter?.name; + const sort_by = Filter?.sort_by; + + const response = useGetAll${capitalizeFirstLetter(fileName)}({ + pagination: true, + ...filterState, + name: filterState.name || name, + sort_by, + }); + + return ; +}; + +export default App; + +` + +fs.writeFileSync('src/Pages/'+fileName+"/"+"Table.tsx", +FileContainer +); + +console.log(`File "${fileName}" generated successfully.`); + + +function capitalizeFirstLetter(word) { + return (word).charAt(0).toUpperCase() + (word).slice(1); +} diff --git a/src/Extensions/FileGenerator/generateType.js b/src/Extensions/FileGenerator/generateType.js new file mode 100644 index 0000000..8ea1d57 --- /dev/null +++ b/src/Extensions/FileGenerator/generateType.js @@ -0,0 +1,52 @@ +const fs = require('fs'); +const path = require('path'); + +// Get the file name from the command line arguments +const fileName = process.argv[2]; + +// Check if a file name is provided +if (!fileName) { + console.error('Please provide a file name.'); + process.exit(1); +} + +// Define the directory path +const directoryPath = path.join('src', 'types'); + +// Ensure the directory exists +if (!fs.existsSync(directoryPath)) { + fs.mkdirSync(directoryPath, { recursive: true }); // Create the directory if it doesn't exist +} + +const FileContainer = ` + +import { Nullable } from "./App"; + +// Define the ${capitalizeFirstLetter(fileName)} interface + +export interface ${capitalizeFirstLetter(fileName)} { + id: number; + name: string; + image: string; +} + +export interface InitialValues { + id: number; + name: string; + image: string; +} + +export type ${capitalizeFirstLetter(fileName)}InitialValues = Partial>; + +`; + +fs.writeFileSync( + path.join(directoryPath, `${capitalizeFirstLetter(fileName)}.ts`), + FileContainer +); + +console.log(`File "${fileName}" generated successfully.`); + +function capitalizeFirstLetter(word) { + return word.charAt(0).toUpperCase() + word.slice(1); +}