From 1e5b935bb2b77945ab784081e157890dc3ec33fc Mon Sep 17 00:00:00 2001 From: moaz_dw Date: Fri, 22 Nov 2024 01:59:39 +0300 Subject: [PATCH 1/4] generator --- package-lock.json | 42 ++++++ package.json | 8 +- .../FileGenerator/generateAddModal.js | 68 ++++++++++ src/Extensions/FileGenerator/generateApi.js | 45 +++++++ .../FileGenerator/generateColumn.js | 122 ++++++++++++++++++ .../FileGenerator/generateDashboard.js | 37 ++++++ .../FileGenerator/generateEditModal.js | 67 ++++++++++ .../FileGenerator/generateFilterForm.js | 47 +++++++ .../FileGenerator/generateFormUtils.js | 51 ++++++++ src/Extensions/FileGenerator/generateIndex.js | 44 +++++++ .../FileGenerator/generateModelEnum.js | 63 +++++++++ .../FileGenerator/generateModelForm.js | 38 ++++++ src/Extensions/FileGenerator/generatePage.js | 90 +++++++++++++ .../FileGenerator/generateParamsEnum.js | 39 ++++++ src/Extensions/FileGenerator/generateRoute.js | 83 ++++++++++++ src/Extensions/FileGenerator/generateTable.js | 58 +++++++++ src/Extensions/FileGenerator/generateType.js | 52 ++++++++ 17 files changed, 953 insertions(+), 1 deletion(-) create mode 100644 src/Extensions/FileGenerator/generateAddModal.js create mode 100644 src/Extensions/FileGenerator/generateApi.js create mode 100644 src/Extensions/FileGenerator/generateColumn.js create mode 100644 src/Extensions/FileGenerator/generateDashboard.js create mode 100644 src/Extensions/FileGenerator/generateEditModal.js create mode 100644 src/Extensions/FileGenerator/generateFilterForm.js create mode 100644 src/Extensions/FileGenerator/generateFormUtils.js create mode 100644 src/Extensions/FileGenerator/generateIndex.js create mode 100644 src/Extensions/FileGenerator/generateModelEnum.js create mode 100644 src/Extensions/FileGenerator/generateModelForm.js create mode 100644 src/Extensions/FileGenerator/generatePage.js create mode 100644 src/Extensions/FileGenerator/generateParamsEnum.js create mode 100644 src/Extensions/FileGenerator/generateRoute.js create mode 100644 src/Extensions/FileGenerator/generateTable.js create mode 100644 src/Extensions/FileGenerator/generateType.js diff --git a/package-lock.json b/package-lock.json index 28f6d37..ab6f08b 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" }, @@ -5275,6 +5276,19 @@ "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==", + "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==" + }, "node_modules/ejs": { "version": "3.1.10", "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", @@ -14656,6 +14670,15 @@ "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==", + "dependencies": { + "dotenv": "8.2.0", + "dotenv-expand": "5.1.0" + } + }, "node_modules/void-elements": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz", @@ -18867,6 +18890,16 @@ "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==" + }, + "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==" + }, "ejs": { "version": "3.1.10", "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", @@ -25325,6 +25358,15 @@ "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==", + "requires": { + "dotenv": "8.2.0", + "dotenv-expand": "5.1.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 1504d36..0b4e594 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", "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/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..8a86060 --- /dev/null +++ b/src/Extensions/FileGenerator/generateDashboard.js @@ -0,0 +1,37 @@ +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}` +]; + +// 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..00050a1 --- /dev/null +++ b/src/Extensions/FileGenerator/generateModelEnum.js @@ -0,0 +1,63 @@ +const fs = require("fs"); +const path = require("path"); + +const MODEL_ENUM_FILE = "./src/enums/Model.ts"; // Path to the ModelEnum file + +// The new enums to be added +const newEnums = [ + { key: "TEST_EDIT", value: "TEST.edit" }, + { key: "TEST_ADD", value: "TEST.add" }, + { key: "TEST_DELETE", value: "TEST.delete" }, +]; + +// Function to generate the new enum entries +const generateEnumEntries = (enums) => { + return enums + .map(({ key, value }) => ` ${key} = "${value}",`) + .join("\n"); +}; + +// Function to update the ModelEnum 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 ModelEnum file + const fileContent = fs.readFileSync(MODEL_ENUM_FILE, "utf-8"); + + // Extract the current enums + const existingEnums = new Set(); + const enumRegex = /(\w+)\s*=\s*"([\w.]+)"/g; + let match; + while ((match = enumRegex.exec(fileContent)) !== null) { + existingEnums.add(match[1]); + } + + // Filter out enums that already exist + const uniqueEnums = newEnums.filter(({ key }) => !existingEnums.has(key)); + + if (uniqueEnums.length === 0) { + console.log("No new enums to add. All enums already exist."); + return; + } + + // Generate the new enum entries + const newEnumEntries = generateEnumEntries(uniqueEnums); + + // Insert the new enums before the closing brace of the ModelEnum + const updatedContent = fileContent.replace( + /export\s+enum\s+ModelEnum\s*{([\s\S]*?)}/, + (match, existingContent) => { + return `export enum ModelEnum {\n${existingContent.trim()}\n\n${newEnumEntries}\n}`; + } + ); + + // Write the updated content back to the file + fs.writeFileSync(MODEL_ENUM_FILE, updatedContent, "utf-8"); + + console.log("ModelEnum file updated successfully!"); +}; + +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); +} From 3d98fc56d1f12ff2f91160dbdb4b6ae670649451 Mon Sep 17 00:00:00 2001 From: moaz_dw Date: Fri, 22 Nov 2024 02:06:36 +0300 Subject: [PATCH 2/4] fix generator --- .../FileGenerator/generateModelEnum.js | 42 +++++++------------ 1 file changed, 16 insertions(+), 26 deletions(-) diff --git a/src/Extensions/FileGenerator/generateModelEnum.js b/src/Extensions/FileGenerator/generateModelEnum.js index 00050a1..ed92765 100644 --- a/src/Extensions/FileGenerator/generateModelEnum.js +++ b/src/Extensions/FileGenerator/generateModelEnum.js @@ -1,63 +1,53 @@ const fs = require("fs"); const path = require("path"); -const MODEL_ENUM_FILE = "./src/enums/Model.ts"; // Path to the ModelEnum file +const MODEL_ENUM_FILE = "./src/enums/Model.ts"; // Path to the ModalEnum file -// The new enums to be added const newEnums = [ { key: "TEST_EDIT", value: "TEST.edit" }, { key: "TEST_ADD", value: "TEST.add" }, { key: "TEST_DELETE", value: "TEST.delete" }, ]; -// Function to generate the new enum entries -const generateEnumEntries = (enums) => { - return enums - .map(({ key, value }) => ` ${key} = "${value}",`) - .join("\n"); -}; +const enumName = "ModalEnum"; // Update this to match your enum name + +const generateEnumEntries = (enums) => + enums.map(({ key, value }) => ` ${key} = "${value}",`).join("\n"); -// Function to update the ModelEnum 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 ModelEnum file const fileContent = fs.readFileSync(MODEL_ENUM_FILE, "utf-8"); - // Extract the current enums - const existingEnums = new Set(); - const enumRegex = /(\w+)\s*=\s*"([\w.]+)"/g; - let match; - while ((match = enumRegex.exec(fileContent)) !== null) { - existingEnums.add(match[1]); - } + const existingEnums = new Set( + Array.from(fileContent.matchAll(/(\w+)\s*=\s*"([\w.]+)"/g), (match) => + match[1].toLowerCase() + ) + ); - // Filter out enums that already exist - const uniqueEnums = newEnums.filter(({ key }) => !existingEnums.has(key)); + const uniqueEnums = newEnums.filter( + ({ key }) => !existingEnums.has(key.toLowerCase()) + ); if (uniqueEnums.length === 0) { console.log("No new enums to add. All enums already exist."); return; } - // Generate the new enum entries const newEnumEntries = generateEnumEntries(uniqueEnums); - // Insert the new enums before the closing brace of the ModelEnum const updatedContent = fileContent.replace( - /export\s+enum\s+ModelEnum\s*{([\s\S]*?)}/, + new RegExp(`export\\s+enum\\s+${enumName}\\s*{([\\s\\S]*?)}`), (match, existingContent) => { - return `export enum ModelEnum {\n${existingContent.trim()}\n\n${newEnumEntries}\n}`; + return `export enum ${enumName} {\n${existingContent.trim()}\n\n${newEnumEntries}\n}`; } ); - // Write the updated content back to the file fs.writeFileSync(MODEL_ENUM_FILE, updatedContent, "utf-8"); - - console.log("ModelEnum file updated successfully!"); + console.log("ModalEnum file updated successfully!"); }; updateModelEnumFile(); From 094b89bdd1cd860ab62f8863acab805d0abc703b Mon Sep 17 00:00:00 2001 From: moaz_dw Date: Sat, 23 Nov 2024 19:00:32 +0300 Subject: [PATCH 3/4] ability generator --- .../FileGenerator/generateAbility.js | 84 +++++++++++++++++++ .../FileGenerator/generateDashboard.js | 4 +- .../FileGenerator/generateModelEnum.js | 72 ++++++++++------ 3 files changed, 133 insertions(+), 27 deletions(-) create mode 100644 src/Extensions/FileGenerator/generateAbility.js 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/generateDashboard.js b/src/Extensions/FileGenerator/generateDashboard.js index 8a86060..600cb88 100644 --- a/src/Extensions/FileGenerator/generateDashboard.js +++ b/src/Extensions/FileGenerator/generateDashboard.js @@ -17,7 +17,9 @@ const commands = [ `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/generateModelEnum.js ${fileName}`, + `node src/Extensions/FileGenerator/generateAbility.js ${fileName}` + ]; // Execute each command sequentially diff --git a/src/Extensions/FileGenerator/generateModelEnum.js b/src/Extensions/FileGenerator/generateModelEnum.js index ed92765..eeb9b98 100644 --- a/src/Extensions/FileGenerator/generateModelEnum.js +++ b/src/Extensions/FileGenerator/generateModelEnum.js @@ -1,53 +1,73 @@ const fs = require("fs"); const path = require("path"); -const MODEL_ENUM_FILE = "./src/enums/Model.ts"; // Path to the ModalEnum file +// Path to the ModalEnum file +const MODEL_ENUM_FILE = "./src/enums/Model.ts"; -const newEnums = [ - { key: "TEST_EDIT", value: "TEST.edit" }, - { key: "TEST_ADD", value: "TEST.add" }, - { key: "TEST_DELETE", value: "TEST.delete" }, -]; +// 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 +}; -const enumName = "ModalEnum"; // Update this to match your enum name +// Take the name dynamically from the terminal +const nameInput = process.argv[2]; -const generateEnumEntries = (enums) => - enums.map(({ key, value }) => ` ${key} = "${value}",`).join("\n"); +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"); - const existingEnums = new Set( - Array.from(fileContent.matchAll(/(\w+)\s*=\s*"([\w.]+)"/g), (match) => - match[1].toLowerCase() - ) + // 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) ); - const uniqueEnums = newEnums.filter( - ({ key }) => !existingEnums.has(key.toLowerCase()) - ); - - if (uniqueEnums.length === 0) { - console.log("No new enums to add. All enums already exist."); + if (duplicateKeys.length > 0) { + console.log(`The keys ${duplicateKeys.join(", ")} already exist. No changes made.`); return; } - const newEnumEntries = generateEnumEntries(uniqueEnums); - + // Inject the new entries before the closing brace of the enum const updatedContent = fileContent.replace( - new RegExp(`export\\s+enum\\s+${enumName}\\s*{([\\s\\S]*?)}`), - (match, existingContent) => { - return `export enum ${enumName} {\n${existingContent.trim()}\n\n${newEnumEntries}\n}`; - } + /(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("ModalEnum file updated successfully!"); + console.log(`Enum entries for "${nameInput}" added successfully to ModalEnum.`); }; updateModelEnumFile(); From e5443ba3fa8896d5ee22e8f8a8dc7e9e8f8ab6c0 Mon Sep 17 00:00:00 2001 From: moaz_dw Date: Tue, 3 Dec 2024 15:56:49 +0300 Subject: [PATCH 4/4] fix generator --- src/Extensions/FileGenerator/generateDashboard.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Extensions/FileGenerator/generateDashboard.js b/src/Extensions/FileGenerator/generateDashboard.js index 600cb88..5b514a1 100644 --- a/src/Extensions/FileGenerator/generateDashboard.js +++ b/src/Extensions/FileGenerator/generateDashboard.js @@ -16,9 +16,9 @@ const commands = [ `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}` + // `node src/Extensions/FileGenerator/generateRoute.js ${fileName}`, + // `node src/Extensions/FileGenerator/generateModelEnum.js ${fileName}`, + // `node src/Extensions/FileGenerator/generateAbility.js ${fileName}` ];