diff --git a/api/src/config/dev.config.ts b/api/src/config/dev.config.ts index 00f69565a..cdd2363dc 100644 --- a/api/src/config/dev.config.ts +++ b/api/src/config/dev.config.ts @@ -12,4 +12,4 @@ export const devConfig = { }, LOG_FILE_PATH: process.platform === "win32" ? ".\\combine.log" : "./combine.log", // Replace with the actual path to your log file -}; \ No newline at end of file +}; diff --git a/api/src/config/index.ts b/api/src/config/index.ts index 4f1523603..11f43c759 100644 --- a/api/src/config/index.ts +++ b/api/src/config/index.ts @@ -3,10 +3,16 @@ import path from "path"; import { prodConfig } from "./prod.config.js"; import { devConfig } from "./dev.config.js"; +/** + * Loads the environment variables from the .env file. + */ dotenv.config({ path: path.resolve(process.cwd(), `${process.env.NODE_ENV}.env`), }); +/** + * Configuration type for the application. + */ export type ConfigType = { APP_TOKEN_EXP: string; APP_TOKEN_KEY: string; @@ -28,6 +34,9 @@ export type ConfigType = { }; }; +/** + * Configuration object for the application. + */ export const config: ConfigType = { APP_TOKEN_EXP: "1d", PORT: process.env.PORT!, diff --git a/api/src/controllers/auth.controller.ts b/api/src/controllers/auth.controller.ts index 55e092e11..bdcbe1804 100644 --- a/api/src/controllers/auth.controller.ts +++ b/api/src/controllers/auth.controller.ts @@ -1,11 +1,23 @@ import { Request, Response } from "express"; import { authService } from "../services/auth.service.js"; +/** + * Handles the login request. + * + * @param req - The request object. + * @param res - The response object. + */ const login = async (req: Request, res: Response) => { const resp = await authService.login(req); res.status(resp?.status).json(resp?.data); }; +/** + * Handles the request for sending an SMS. + * + * @param req - The request object. + * @param res - The response object. + */ const RequestSms = async (req: Request, res: Response) => { const resp = await authService.requestSms(req); res.status(resp.status).json(resp.data); diff --git a/api/src/controllers/migration.controller.ts b/api/src/controllers/migration.controller.ts index 9816c5fe5..5d4d62498 100644 --- a/api/src/controllers/migration.controller.ts +++ b/api/src/controllers/migration.controller.ts @@ -1,11 +1,25 @@ import { Request, Response } from "express"; import { migrationService } from "../services/migration.service.js"; +/** + * Creates a test stack. + * + * @param req - The request object. + * @param res - The response object. + * @returns A promise that resolves to void. + */ const createTestStack = async (req: Request, res: Response): Promise => { const resp = await migrationService.createTestStack(req); res.status(200).json(resp); }; +/** + * Deletes the test stack. + * + * @param {Request} req - The request object. + * @param {Response} res - The response object. + * @returns {Promise} - A Promise that resolves when the stack is deleted. + */ const deleteTestStack = async (req: Request, res: Response): Promise => { const resp = await migrationService.deleteTestStack(req); res.status(200).json(resp); diff --git a/api/src/controllers/org.controller.ts b/api/src/controllers/org.controller.ts index a826ab489..ac01c6964 100644 --- a/api/src/controllers/org.controller.ts +++ b/api/src/controllers/org.controller.ts @@ -1,21 +1,49 @@ import { Request, Response } from "express"; import { orgService } from "../services/org.service.js"; +/** + * Retrieves all stacks. + * + * @param {Request} req - The request object. + * @param {Response} res - The response object. + * @returns {Promise} - A promise that resolves when the operation is complete. + */ const getAllStacks = async (req: Request, res: Response) => { const resp = await orgService.getAllStacks(req); res.status(resp?.status).json(resp?.data); }; +/** + * Creates a stack. + * + * @param {Request} req - The request object. + * @param {Response} res - The response object. + * @returns {Promise} - A promise that resolves when the stack is created. + */ const createStack = async (req: Request, res: Response) => { const resp = await orgService.createStack(req); res.status(resp.status).json(resp.data); }; +/** + * Retrieves the locales for an organization. + * + * @param {Request} req - The request object. + * @param {Response} res - The response object. + * @returns {Promise} - A promise that resolves when the locales are retrieved. + */ const getLocales = async (req: Request, res: Response) => { const resp = await orgService.getLocales(req); res.status(resp.status).json(resp.data); }; +/** + * Retrieves the stack status. + * + * @param req - The request object. + * @param res - The response object. + * @returns A Promise that resolves to the stack status response. + */ const getStackStatus = async (req: Request, res: Response) => { const resp = await orgService.getStackStatus(req); res.status(resp.status).json(resp.data); diff --git a/api/src/controllers/projects.contentMapper.controller.ts b/api/src/controllers/projects.contentMapper.controller.ts index 6a22299c4..0839935fe 100644 --- a/api/src/controllers/projects.contentMapper.controller.ts +++ b/api/src/controllers/projects.contentMapper.controller.ts @@ -1,18 +1,46 @@ import { Request, Response } from "express"; import { contentMapperService } from "../services/contentMapper.service.js"; +/** + * Handles the PUT request to update test data. + * + * @param req - The request object. + * @param res - The response object. + * @returns A Promise that resolves to void. + */ const putTestData = async (req: Request, res: Response): Promise => { const resp = await contentMapperService.putTestData(req); res.status(200).json(resp); }; +/** + * Retrieves the content types from the content mapper service and sends the response as JSON. + * + * @param req - The Express request object. + * @param res - The Express response object. + * @returns A Promise that resolves to void. + */ const getContentTypes = async (req: Request, res: Response): Promise => { const resp = await contentMapperService.getContentTypes(req); res.status(200).json(resp); }; +/** + * Retrieves the field mapping for a given request and sends the response as JSON. + * + * @param req - The request object. + * @param res - The response object. + * @returns A Promise that resolves to void. + */ const getFieldMapping = async (req: Request, res: Response): Promise => { const resp = await contentMapperService.getFieldMapping(req); res.status(200).json(resp); }; +/** + * Retrieves the existing content types. + * + * @param {Request} req - The request object. + * @param {Response} res - The response object. + * @returns {Promise} - A promise that resolves when the operation is complete. + */ const getExistingContentTypes = async ( req: Request, res: Response @@ -20,6 +48,13 @@ const getExistingContentTypes = async ( const resp = await contentMapperService.getExistingContentTypes(req); res.status(201).json(resp); }; +/** + * Updates the content type fields. + * + * @param {Request} req - The request object. + * @param {Response} res - The response object. + * @returns {Promise} A promise that resolves when the content type fields are updated. + */ const putContentTypeFields = async ( req: Request, res: Response @@ -27,6 +62,13 @@ const putContentTypeFields = async ( const resp = await contentMapperService.updateContentType(req); res.status(200).json(resp); }; +/** + * Resets the content type to its initial mapping. + * + * @param req - The request object. + * @param res - The response object. + * @returns A Promise that resolves to void. + */ const resetContentType = async (req: Request, res: Response): Promise => { const resp = await contentMapperService.resetToInitialMapping(req); res.status(200).json(resp); @@ -37,11 +79,28 @@ const resetContentType = async (req: Request, res: Response): Promise => { // res.status(200).json(resp); // }; -const removeContentMapper = async (req: Request, res: Response): Promise => { +/** + * Removes a content mapper. + * + * @param {Request} req - The request object. + * @param {Response} res - The response object. + * @returns {Promise} - A promise that resolves when the content mapper is removed. + */ +const removeContentMapper = async ( + req: Request, + res: Response +): Promise => { const resp = await contentMapperService.removeContentMapper(req); res.status(200).json(resp); -} +}; +/** + * Retrieves single content types. + * + * @param req - The request object. + * @param res - The response object. + * @returns A Promise that resolves to void. + */ const getSingleContentTypes = async ( req: Request, res: Response @@ -59,5 +118,5 @@ export const contentMapperController = { resetContentType, // removeMapping, getSingleContentTypes, - removeContentMapper + removeContentMapper, }; diff --git a/api/src/controllers/projects.controller.ts b/api/src/controllers/projects.controller.ts index dbe1abb97..3e7be0316 100644 --- a/api/src/controllers/projects.controller.ts +++ b/api/src/controllers/projects.controller.ts @@ -1,64 +1,154 @@ import { Request, Response } from "express"; import { projectService } from "../services/projects.service.js"; +/** + * Retrieves all projects. + * + * @param req - The request object. + * @param res - The response object. + * @returns A promise that resolves to void. + */ const getAllProjects = async (req: Request, res: Response): Promise => { const allProjects = await projectService.getAllProjects(req); res.status(200).json(allProjects); }; +/** + * Retrieves a project based on the request. + * + * @param req - The request object. + * @param res - The response object. + * @returns A Promise that resolves to void. + */ const getProject = async (req: Request, res: Response): Promise => { const project = await projectService.getProject(req); res.status(200).json(project); }; +/** + * Creates a new project. + * + * @param req - The request object. + * @param res - The response object. + * @returns A Promise that resolves to void. + */ const createProject = async (req: Request, res: Response): Promise => { const project = await projectService.createProject(req); res.status(201).json(project); }; +/** + * Updates a project. + * + * @param req - The request object. + * @param res - The response object. + * @returns A Promise that resolves to void. + */ const updateProject = async (req: Request, res: Response): Promise => { const project = await projectService.updateProject(req); res.status(200).json(project); }; +/** + * Updates the legacy CMS for a project. + * + * @param {Request} req - The request object. + * @param {Response} res - The response object. + * @returns {Promise} - A promise that resolves when the update is complete. + */ const updateLegacyCMS = async (req: Request, res: Response) => { const resp = await projectService.updateLegacyCMS(req); res.status(resp.status).json(resp.data); }; +/** + * Updates the affix for a project. + * + * @param {Request} req - The request object. + * @param {Response} res - The response object. + * @returns {Promise} - A promise that resolves when the affix is updated. + */ const updateAffix = async (req: Request, res: Response) => { const resp = await projectService.updateAffix(req); res.status(resp.status).json(resp.data); }; +/** + * Handles the affix confirmation request. + * + * @param req - The request object. + * @param res - The response object. + * @returns A Promise that resolves to the response data. + */ const affixConfirmation = async (req: Request, res: Response) => { const resp = await projectService.affixConfirmation(req); res.status(resp.status).json(resp.data); }; +/** + * Updates the file format for a project. + * + * @param {Request} req - The request object. + * @param {Response} res - The response object. + * @returns {Promise} - A promise that resolves when the file format is updated. + */ const updateFileFormat = async (req: Request, res: Response) => { const resp = await projectService.updateFileFormat(req); res.status(resp.status).json(resp.data); }; +/** + * Handles the file format confirmation request. + * + * @param req - The request object. + * @param res - The response object. + */ const fileformatConfirmation = async (req: Request, res: Response) => { const resp = await projectService.fileformatConfirmation(req); res.status(resp.status).json(resp.data); }; +/** + * Updates the destination stack for a project. + * + * @param {Request} req - The request object. + * @param {Response} res - The response object. + * @returns {Promise} - A promise that resolves when the destination stack is updated. + */ const updateDestinationStack = async (req: Request, res: Response) => { const resp = await projectService.updateDestinationStack(req); res.status(resp.status).json(resp.data); }; +/** + * Updates the current step of a project. + * + * @param {Request} req - The request object. + * @param {Response} res - The response object. + * @returns {Promise} - A promise that resolves with the updated project. + */ const updateCurrentStep = async (req: Request, res: Response) => { const project = await projectService.updateCurrentStep(req); res.status(200).json(project); }; +/** + * Deletes a project. + * + * @param req - The request object. + * @param res - The response object. + * @returns A Promise that resolves to void. + */ const deleteProject = async (req: Request, res: Response): Promise => { const project = await projectService.deleteProject(req); res.status(200).json(project); }; +/** + * Reverts a project. + * + * @param req - The request object. + * @param res - The response object. + * @returns A Promise that resolves to void. + */ const revertProject = async (req: Request, res: Response): Promise => { const project = await projectService.revertProject(req); res.status(project.status).json(project); diff --git a/api/src/controllers/user.controller.ts b/api/src/controllers/user.controller.ts index d74936372..b0c883740 100644 --- a/api/src/controllers/user.controller.ts +++ b/api/src/controllers/user.controller.ts @@ -1,6 +1,13 @@ import { Request, Response } from "express"; import { userService } from "../services/user.service.js"; +/** + * Retrieves the user profile. + * + * @param {Request} req - The request object. + * @param {Response} res - The response object. + * @returns {Promise} - A promise that resolves when the user profile is retrieved. + */ const getUserProfile = async (req: Request, res: Response) => { const resp = await userService.getUserProfile(req); res.status(resp?.status).json(resp?.data); diff --git a/api/src/database.ts b/api/src/database.ts index 859a3b0eb..8682aac4e 100644 --- a/api/src/database.ts +++ b/api/src/database.ts @@ -2,6 +2,12 @@ import logger from "./utils/logger.js"; import fs from "fs"; +/** + * Connects to the database. + * If the database folder does not exist, it creates it. + * @returns {Promise} A promise that resolves when the connection is successful. + * @throws {Error} If there is an error while connecting to the database. + */ const connectToDatabase = async () => { try { //check if the database folder exists diff --git a/api/src/middlewares/auth.middleware.ts b/api/src/middlewares/auth.middleware.ts index 17987a3eb..1e1499d28 100644 --- a/api/src/middlewares/auth.middleware.ts +++ b/api/src/middlewares/auth.middleware.ts @@ -4,6 +4,13 @@ import jwt from "jsonwebtoken"; import { config } from "../config/index.js"; import { HTTP_CODES } from "../constants/index.js"; +/** + * Middleware function to authenticate the user. + * + * @param req - The Express request object. + * @param res - The Express response object. + * @param next - The next middleware function. + */ export const authenticateUser = ( req: Request, res: Response, @@ -17,6 +24,11 @@ export const authenticateUser = ( .status(status) .json({ status, message: "Unauthorized - Token missing" }); + /* this middleware function verifies the provided JWT token, + handles any errors that may occur during verification, + attaches the decoded token payload to the request object, + and then passes control to the next middleware or request handler. + */ jwt.verify(token, config.APP_TOKEN_KEY, (err, payload) => { if (err) return res diff --git a/api/src/middlewares/auth.uploadService.middleware.ts b/api/src/middlewares/auth.uploadService.middleware.ts index c23458493..cb1795302 100644 --- a/api/src/middlewares/auth.uploadService.middleware.ts +++ b/api/src/middlewares/auth.uploadService.middleware.ts @@ -2,6 +2,16 @@ import { Request, Response, NextFunction } from "express"; import { HTTP_CODES } from "../constants/index.js"; import { config } from "../config/index.js"; +/** + * Middleware function to authenticate the upload service. + * Checks if the provided secret key matches the configured file upload key. + * If the key is valid, the request is allowed to proceed to the next middleware or route handler. + * If the key is invalid, an unauthorized response is sent. + * + * @param req - The Express Request object. + * @param res - The Express Response object. + * @param next - The Express NextFunction to pass control to the next middleware or route handler. + */ export const authenticateUploadService = ( req: Request, res: Response, diff --git a/api/src/middlewares/error.middleware.ts b/api/src/middlewares/error.middleware.ts index 199152893..a2fb1138c 100644 --- a/api/src/middlewares/error.middleware.ts +++ b/api/src/middlewares/error.middleware.ts @@ -2,6 +2,13 @@ import { Request, Response, NextFunction } from "express"; import { AppError } from "../utils/custom-errors.utils.js"; import logger from "../utils/logger.js"; +/** + * Middleware function to handle errors in the application. + * @param err - The error object. + * @param req - The Express request object. + * @param res - The Express response object. + * @param next - The next middleware function. + */ export const errorMiddleware = ( err: Error, req: Request, diff --git a/api/src/middlewares/logger.middleware.ts b/api/src/middlewares/logger.middleware.ts index 0ccecbf70..df9568339 100644 --- a/api/src/middlewares/logger.middleware.ts +++ b/api/src/middlewares/logger.middleware.ts @@ -2,6 +2,16 @@ import expressWinston from "express-winston"; import logger from "../utils/logger.js"; //Logger Middleware to log every request +/** + * Express middleware for logging HTTP requests. + * + * @remarks + * This middleware uses `express-winston` to log HTTP requests with the specified options. + * + * @param req - The Express request object. + * @param res - The Express response object. + * @param next - The next middleware function. + */ const loggerMiddleware = expressWinston.logger({ level: "info", colorize: true, diff --git a/api/src/middlewares/req-headers.middleware.ts b/api/src/middlewares/req-headers.middleware.ts index 6a71f52b5..1c08008ab 100644 --- a/api/src/middlewares/req-headers.middleware.ts +++ b/api/src/middlewares/req-headers.middleware.ts @@ -1,5 +1,13 @@ import { Request, Response, NextFunction } from "express"; +/** + * Middleware function to handle request headers. + * Adds necessary headers for CORS support and handles OPTIONS requests. + * + * @param req - The Express Request object. + * @param res - The Express Response object. + * @param next - The next middleware function. + */ export const requestHeadersMiddleware = ( req: Request, res: Response, diff --git a/api/src/middlewares/unmatched-routes.middleware.ts b/api/src/middlewares/unmatched-routes.middleware.ts index 02cd8530a..cc6db151f 100644 --- a/api/src/middlewares/unmatched-routes.middleware.ts +++ b/api/src/middlewares/unmatched-routes.middleware.ts @@ -1,6 +1,12 @@ import { Request, Response } from "express"; import { HTTP_CODES, HTTP_TEXTS } from "../constants/index.js"; +/** + * Middleware function to handle unmatched routes. + * + * @param req - The Express request object. + * @param res - The Express response object. + */ export const unmatchedRoutesMiddleware = (req: Request, res: Response) => { const status = HTTP_CODES.NOT_FOUND; res.status(status).json({ diff --git a/api/src/models/FieldMapper.ts b/api/src/models/FieldMapper.ts index f37f6bff4..8e25c6d2d 100644 --- a/api/src/models/FieldMapper.ts +++ b/api/src/models/FieldMapper.ts @@ -1,6 +1,9 @@ import { JSONFile } from "lowdb/node"; import LowWithLodash from "../utils/lowdb-lodash.utils.js"; +/** + * Represents the advanced configuration options for a field mapper. + */ interface Advanced { validationRegex: string; Mandatory: boolean; @@ -9,9 +12,13 @@ interface Advanced { NonLocalizable: boolean; } +/** + * Represents a field mapper object. + */ interface FieldMapper { field_mapper: { id: string; + projectId: string; uid: string; otherCmsField: string; otherCmsType: string; @@ -27,6 +34,9 @@ interface FieldMapper { const defaultData: FieldMapper = { field_mapper: [] }; +/** + * Represents the database instance for the FieldMapper model. + */ const db = new LowWithLodash( new JSONFile("database/field-mapper.json"), defaultData diff --git a/api/src/models/authentication.ts b/api/src/models/authentication.ts index 57bfd6f41..e50d8bbc5 100644 --- a/api/src/models/authentication.ts +++ b/api/src/models/authentication.ts @@ -1,6 +1,9 @@ // src/models/Authentication.ts import { JSONFile } from "lowdb/node"; import LowWithLodash from "../utils/lowdb-lodash.utils.js"; +/** + * Represents the authentication document. + */ interface AuthenticationDocument { users: { user_id: string; @@ -13,6 +16,9 @@ interface AuthenticationDocument { const defaultData: AuthenticationDocument = { users: [] }; +/** + * Represents the database instance for authentication data. + */ const db = new LowWithLodash( new JSONFile("database/authentication.json"), defaultData diff --git a/api/src/models/contentTypesMapper-lowdb.ts b/api/src/models/contentTypesMapper-lowdb.ts index 77c3897a9..cf7c667f4 100644 --- a/api/src/models/contentTypesMapper-lowdb.ts +++ b/api/src/models/contentTypesMapper-lowdb.ts @@ -1,16 +1,64 @@ import { JSONFile } from "lowdb/node"; import LowWithLodash from "../utils/lowdb-lodash.utils.js"; +/** + * Represents a content type mapper. + */ export interface ContentTypesMapper { + /** + * The unique identifier of the content type mapper. + */ id: string; + + /** + * The unique identifier of the project. + */ + projectId: string; + + /** + * The title of the content type in the other CMS. + */ otherCmsTitle: string; + + /** + * The unique identifier of the content type in the other CMS. + */ otherCmsUid: string; + + /** + * Indicates whether the content type has been updated. + */ isUpdated: boolean; + + /** + * The date when the content type was last updated. + */ updateAt: Date; + + /** + * The title of the content type in Contentstack. + */ contentstackTitle: string; + + /** + * The unique identifier of the content type in Contentstack. + */ contentstackUid: string; + + /** + * The status of the content type. + */ status: number; + + /** + * The field mapping for the content type. + */ fieldMapping: []; + + /** + * The type of the content type. + */ + type: string; } // interface ContentTypesMapper { @@ -19,12 +67,18 @@ export interface ContentTypesMapper { // contentTypes: [contentTypes]; // } +/** + * Represents a document containing content type mappers. + */ interface ContentTypeMapperDocument { ContentTypesMappers: ContentTypesMapper[]; } const defaultData: ContentTypeMapperDocument = { ContentTypesMappers: [] }; +/** + * Represents the database instance for the content types mapper. + */ const db = new LowWithLodash( new JSONFile("database/contentTypesMapper.json"), defaultData diff --git a/api/src/models/project-lowdb.ts b/api/src/models/project-lowdb.ts index 233e7f5d9..74bfb6894 100644 --- a/api/src/models/project-lowdb.ts +++ b/api/src/models/project-lowdb.ts @@ -1,6 +1,9 @@ import { JSONFile } from "lowdb/node"; import LowWithLodash from "../utils/lowdb-lodash.utils.js"; +/** + * Represents the LegacyCMS object. + */ interface LegacyCMS { cms: string; affix: string; @@ -24,11 +27,17 @@ interface LegacyCMS { is_localPath: boolean; } +/** + * Represents an execution log. + */ interface ExecutionLog { log_url: string; date: Date; } +/** + * Represents a project. + */ interface Project { id: string; region: string; @@ -50,6 +59,8 @@ interface Project { created_at: string; updated_at: string; isDeleted: boolean; + isNewStack: boolean; + newStackId: string; } interface ProjectDocument { @@ -58,6 +69,9 @@ interface ProjectDocument { const defaultData: ProjectDocument = { projects: [] }; +/** + * Represents the database instance for the project. + */ const db = new LowWithLodash( new JSONFile("database/project.json"), defaultData diff --git a/api/src/models/types.ts b/api/src/models/types.ts index ed5d6d696..41ad999de 100644 --- a/api/src/models/types.ts +++ b/api/src/models/types.ts @@ -1,18 +1,38 @@ +/** + * Represents a user. + */ export interface User { + /** + * The email address of the user. + */ email: string; + + /** + * The password of the user. + */ password: string; } +/** + * Represents the payload of an application token. + */ export interface AppTokenPayload { region: string; user_id: string; } +/** + * Represents the LoginServiceType interface. + * @interface + */ export interface LoginServiceType { data: any; status: number; } +/** + * Represents the migration query type. + */ export interface MigrationQueryType { id: string; org_id: string; diff --git a/api/src/routes/auth.routes.ts b/api/src/routes/auth.routes.ts index 29608bca9..60b1755b9 100644 --- a/api/src/routes/auth.routes.ts +++ b/api/src/routes/auth.routes.ts @@ -3,16 +3,37 @@ import { authController } from "../controllers/auth.controller.js"; import { asyncRouter } from "../utils/async-router.utils.js"; import validator from "../validators/index.js"; +/** + * Express router for handling authentication routes. + */ const router = express.Router(); -// Login route +/** + * Route for user login. + * + * @route POST /user-session + * @group Authentication + * @param {object} req.body - The request body containing user credentials. + * @returns {object} The response object containing user session information. + * @throws {ValidationError} If the request body fails validation. + * @throws {InternalServerError} If an error occurs while processing the request. + */ router.post( "/user-session", validator("auth"), asyncRouter(authController.login) ); -// SMS token route +/** + * Route for requesting SMS token. + * + * @route POST /request-token-sms + * @group Authentication + * @param {object} req.body - The request body containing user information. + * @returns {object} The response object containing the SMS token. + * @throws {ValidationError} If the request body fails validation. + * @throws {InternalServerError} If an error occurs while processing the request. + */ router.post( "/request-token-sms", validator("auth"), diff --git a/api/src/routes/contentMapper.routes.ts b/api/src/routes/contentMapper.routes.ts index a92401e68..f52a5be97 100644 --- a/api/src/routes/contentMapper.routes.ts +++ b/api/src/routes/contentMapper.routes.ts @@ -4,44 +4,73 @@ import { asyncRouter } from "../utils/async-router.utils.js"; const router = express.Router({ mergeParams: true }); -//Developer End Point to create dummy data +/** + * Developer End Point to create dummy data + * @route POST /createDummyData/:projectId + */ router.post( "/createDummyData/:projectId", asyncRouter(contentMapperController.putTestData) ); -//Get ContentTypes List +/** + * Get ContentTypes List + * @route GET /contentTypes/:projectId/:skip/:limit/:searchText? + */ router.get( "/contentTypes/:projectId/:skip/:limit/:searchText?", asyncRouter(contentMapperController.getContentTypes) ); -//Get FieldMapping List + +/** + * Get FieldMapping List + * @route GET /fieldMapping/:contentTypeId/:skip/:limit/:searchText? + */ router.get( - "/fieldMapping/:contentTypeId/:skip/:limit/:searchText?", + "/fieldMapping/:projectId/:contentTypeId/:skip/:limit/:searchText?", asyncRouter(contentMapperController.getFieldMapping) ); -//Get Existing ContentTypes List +/** + * Get Existing ContentTypes List + * @route GET /:projectId + */ router.get( "/:projectId", asyncRouter(contentMapperController.getExistingContentTypes) ); -//Update FieldMapping or contentType + +/** + * Update FieldMapping or contentType + * @route PUT /contentTypes/:orgId/:projectId/:contentTypeId + */ router.put( "/contentTypes/:orgId/:projectId/:contentTypeId", asyncRouter(contentMapperController.putContentTypeFields) ); -//Reset FieldMapping or contentType + +/** + * Reset FieldMapping or contentType + * @route PUT /resetFields/:orgId/:projectId/:contentTypeId + */ router.put( "/resetFields/:orgId/:projectId/:contentTypeId", asyncRouter(contentMapperController.resetContentType) ); -//get Single contenttype data + +/** + * Get Single contenttype data + * @route GET /:projectId/:contentTypeUid + */ router.get( "/:projectId/:contentTypeUid", asyncRouter(contentMapperController.getSingleContentTypes) ); -//remove content mapper + +/** + * Remove content mapper + * @route GET /:orgId/:projectId/content-mapper + */ router.get( "/:orgId/:projectId/content-mapper", asyncRouter(contentMapperController.removeContentMapper) diff --git a/api/src/routes/migration.routes.ts b/api/src/routes/migration.routes.ts index 3239ff985..1288b4e95 100644 --- a/api/src/routes/migration.routes.ts +++ b/api/src/routes/migration.routes.ts @@ -3,14 +3,34 @@ import express from "express"; import { asyncRouter } from "../utils/async-router.utils.js"; import { migrationController } from "../controllers/migration.controller.js"; +/** + * Express router for handling migration routes. + */ const router = express.Router({ mergeParams: true }); -// Create a new project route + +/** + * Route for creating a new test stack. + * @route POST /test-stack/:orgId/:projectId + * @group Migration + * @param {string} orgId - The ID of the organization. + * @param {string} projectId - The ID of the project. + * @returns {Promise} - A promise that resolves when the test stack is created. + */ router.post( "/test-stack/:orgId/:projectId", asyncRouter(migrationController.createTestStack) ); + +/** + * Route for deleting a test stack. + * @route POST /test-stack/:projectId + * @group Migration + * @param {string} projectId - The ID of the project. + * @returns {Promise} - A promise that resolves when the test stack is deleted. + */ router.post( "/test-stack/:projectId", asyncRouter(migrationController.deleteTestStack) ); + export default router; diff --git a/api/src/routes/org.routes.ts b/api/src/routes/org.routes.ts index 2e8a3c571..659861e07 100644 --- a/api/src/routes/org.routes.ts +++ b/api/src/routes/org.routes.ts @@ -3,22 +3,40 @@ import { orgController } from "../controllers/org.controller.js"; import { asyncRouter } from "../utils/async-router.utils.js"; import validator from "../validators/index.js"; +/** + * Express router for handling organization routes. + */ const router = express.Router({ mergeParams: true }); -// GET all org stacks route +/** + * GET all org stacks route. + * @param searchText - Optional parameter for searching stacks. + */ router.get("/stacks/:searchText?", asyncRouter(orgController.getAllStacks)); -// Create a new stack route +/** + * Create a new stack route. + * @param req - Express request object. + * @param res - Express response object. + */ router.post( "/stacks", validator("project"), asyncRouter(orgController.createStack) ); -// GET all contentstack locales route +/** + * GET all contentstack locales route. + * @param req - Express request object. + * @param res - Express response object. + */ router.get("/locales", asyncRouter(orgController.getLocales)); -// GET Content_types count +/** + * GET Content_types count. + * @param req - Express request object. + * @param res - Express response object. + */ router.post( "/stack_status", validator("destination_stack"), diff --git a/api/src/routes/projects.routes.ts b/api/src/routes/projects.routes.ts index 5c7a54099..b56a60c42 100644 --- a/api/src/routes/projects.routes.ts +++ b/api/src/routes/projects.routes.ts @@ -3,6 +3,9 @@ import { projectController } from "../controllers/projects.controller.js"; import { asyncRouter } from "../utils/async-router.utils.js"; import validator from "../validators/index.js"; +/** + * Express router for handling project routes. + */ const router = express.Router({ mergeParams: true }); // GET all projects route diff --git a/api/src/server.ts b/api/src/server.ts index 7e9f0cf42..fbaf4cb60 100644 --- a/api/src/server.ts +++ b/api/src/server.ts @@ -103,4 +103,4 @@ try { } catch (e) { logger.error("Error while starting the server!"); logger.error(e); -} \ No newline at end of file +} diff --git a/api/src/services/auth.service.ts b/api/src/services/auth.service.ts index 5e911d18b..7c79bc25e 100644 --- a/api/src/services/auth.service.ts +++ b/api/src/services/auth.service.ts @@ -13,9 +13,22 @@ import { import AuthenticationModel from "../models/authentication.js"; import logger from "../utils/logger.js"; +/** + * Logs in a user with the provided request data. + * + * @param req - The request object containing user data. + * @returns A promise that resolves to a LoginServiceType object. + * @throws ExceptionFunction if an error occurs during the login process. + */ const login = async (req: Request): Promise => { const srcFun = "Login"; + /* + handles user authentication by making a request to an API, + performing various checks and validations, + updating a model, and generating a JWT token. + It also handles potential errors and logs appropriate messages. + */ try { const userData = req?.body; @@ -115,9 +128,20 @@ const login = async (req: Request): Promise => { } }; +/** + * Sends a request for SMS login token. + * @param req - The request object. + * @returns A promise that resolves to a LoginServiceType object. + * @throws {InternalServerError} If an error occurs while sending the request. + */ const requestSms = async (req: Request): Promise => { const srcFun = "requestSms"; + /* + handles the authentication process by making an HTTP POST request to an API endpoint, + handling any errors that occur, and returning the appropriate response or error data. + It also includes logging functionality to track the execution and potential errors. + */ try { const userData = req?.body; const [err, res] = await safePromise( diff --git a/api/src/services/contentMapper.service.ts b/api/src/services/contentMapper.service.ts index bd2151a07..4949216c6 100644 --- a/api/src/services/contentMapper.service.ts +++ b/api/src/services/contentMapper.service.ts @@ -24,17 +24,34 @@ import ContentTypesMapperModelLowdb from "../models/contentTypesMapper-lowdb.js" import { ContentTypesMapper } from "../models/contentTypesMapper-lowdb.js"; // Developer service to create dummy contentmapping data +/** + * Updates the test data for a given project. + * + * @param req - The request object containing the project ID and content types. + * @returns The updated project data. + */ const putTestData = async (req: Request) => { const projectId = req.params.projectId; - const contentTypes = req.body.contentTypes; + const contentTypes = req.body.contentTypes; await FieldMapperModel.read(); + + /* + this code snippet iterates over an array of contentTypes and performs + some operations on each element. + It creates a new array called fields by mapping over the fieldMapping property of each type in contentTypes. + It generates a unique identifier for each field, pushes it into the fieldIds array, + and returns an object with additional properties. + It then updates the field_mapper property of a data object using the FieldMapperModel.update() function. + Finally, it updates the fieldMapping property of each type in the contentTypes array with the fieldIds array. + */ contentTypes.map((type: any, index: any) => { const fieldIds: string[] = []; const fields = type?.fieldMapping?.map?.((field: any) => { - const id = field?.id || uuidv4(); + const id = field?.id?.replace(/[{}]/g, '')?.toLowerCase() || uuidv4(); + field.id = id; fieldIds.push(id); - return { id, isDeleted: true, ...field }; + return { id, projectId, isDeleted: true, ...field }; }); FieldMapperModel.update((data: any) => { data.field_mapper = [...(data?.field_mapper ?? []), ...fields]; @@ -44,15 +61,24 @@ const putTestData = async (req: Request) => { await ContentTypesMapperModelLowdb.read(); const contentIds: string[] = []; + + /* + this code snippet is iterating over an array called contentTypes and + transforming each element by adding a unique identifier (id) if it doesn't already exist. + The transformed elements are then stored in the contentType variable, + and the generated id values are pushed into the contentIds array. + */ const contentType = contentTypes.map((item: any) => { - const id = item?.id || uuidv4(); + const id = item?.id.replace(/[{}]/g, '')?.toLowerCase() || uuidv4(); + item.id = id; contentIds.push(id); - return { ...item, id }; + return { ...item, id, projectId }; }); await ContentTypesMapperModelLowdb.update((data: any) => { - data.ContentTypesMappers = contentType; + data.ContentTypesMappers = [...(data?.ContentTypesMappers ?? []), ...contentType]; }); + await ProjectModelLowdb.read(); const index = ProjectModelLowdb.chain .get("projects") @@ -72,6 +98,11 @@ const putTestData = async (req: Request) => { return pData; }; +/** + * Retrieves the content types based on the provided request parameters. + * @param req - The request object containing the parameters. + * @returns An object containing the total count and the array of content types. + */ const getContentTypes = async (req: Request) => { const sourceFn = "getContentTypes"; const projectId = req?.params?.projectId; @@ -105,7 +136,7 @@ const getContentTypes = async (req: Request) => { contentMapperId.map((data: any) => { const contentMapperData = ContentTypesMapperModelLowdb.chain .get("ContentTypesMappers") - .find({ id: data }) + .find({ id: data, projectId: projectId }) .value(); content_mapper.push(contentMapperData); }); @@ -134,9 +165,16 @@ const getContentTypes = async (req: Request) => { return { count: totalCount, contentTypes: result }; }; +/** + * Retrieves the field mapping for a given content type. + * @param req - The request object containing the content type ID, skip, limit, and search text. + * @returns An object containing the count of field mappings and the filtered/sliced field mappings. + * @throws BadRequestError if the content type is not found. + */ const getFieldMapping = async (req: Request) => { const srcFunc = "getFieldMapping"; const contentTypeId = req?.params?.contentTypeId; + const projectId = req?.params?.projectId; const skip: any = req?.params?.skip; const limit: any = req?.params?.limit; const search: string = req?.params?.searchText?.toLowerCase(); @@ -149,7 +187,7 @@ const getFieldMapping = async (req: Request) => { const contentType = ContentTypesMapperModelLowdb.chain .get("ContentTypesMappers") - .find({ id: contentTypeId }) + .find({ id: contentTypeId, projectId: projectId }) .value(); if (isEmpty(contentType)) { @@ -165,11 +203,11 @@ const getFieldMapping = async (req: Request) => { const fieldData = contentType.fieldMapping.map((fields: any) => { const fieldMapper = FieldMapperModel.chain .get("field_mapper") - .find({ id: fields }) + .find({ id: fields, projectId: projectId}) .value(); + return fieldMapper; }); - const fieldMapping: any = fieldData; if (!isEmpty(fieldMapping)) { if (search) { @@ -187,6 +225,11 @@ const getFieldMapping = async (req: Request) => { return { count: totalCount, fieldMapping: result }; }; +/** + * Retrieves existing content types for a given project. + * @param req - The request object containing the project ID and token payload. + * @returns An object containing the retrieved content types. + */ const getExistingContentTypes = async (req: Request) => { const projectId = req?.params?.projectId; @@ -232,6 +275,13 @@ const getExistingContentTypes = async (req: Request) => { //Add logic to get Project from DB return { contentTypes }; }; +/** + * Updates the content type based on the provided request. + * @param req - The request object containing the necessary parameters and data. + * @returns An object containing the updated content type. + * @throws BadRequestError if the request is invalid or the content type cannot be updated. + * @throws ExceptionFunction if an error occurs while updating the content type. + */ const updateContentType = async (req: Request) => { const srcFun = "udateContentType"; const { orgId, projectId, contentTypeId } = req.params; @@ -315,7 +365,7 @@ const updateContentType = async (req: Request) => { const updateIndex = ContentTypesMapperModelLowdb.chain .get("ContentTypesMappers") - .findIndex({ id: contentTypeId }) + .findIndex({ id: contentTypeId, projectId: projectId }) .value(); ContentTypesMapperModelLowdb.update((data: any) => { if (updateIndex >= 0) { @@ -366,7 +416,7 @@ const updateContentType = async (req: Request) => { await ContentTypesMapperModelLowdb.read(); const updatedContentType = ContentTypesMapperModelLowdb.chain .get("ContentTypesMappers") - .find({ id: contentTypeId }) + .find({ id: contentTypeId, projectId: projectId }) .value(); return { updatedContentType }; @@ -384,6 +434,15 @@ const updateContentType = async (req: Request) => { ); } }; +/** + * Resets the field mapping and content mapping for a specific content type in a project. + * + * @param req - The request object containing the parameters and body. + * @returns An object with a message indicating the success of the reset operation. + * @throws {BadRequestError} If the project status or current step is not valid for resetting the content mapping. + * @throws {BadRequestError} If the content type is not found or invalid. + * @throws {ExceptionFunction} If an error occurs while resetting the field mapping. + */ const resetToInitialMapping = async (req: Request) => { const srcFunc = "resetToInitialMapping"; const { orgId, projectId, contentTypeId } = req.params; @@ -425,14 +484,14 @@ const resetToInitialMapping = async (req: Request) => { await ContentTypesMapperModelLowdb.read(); const contentTypeData = ContentTypesMapperModelLowdb.chain .get("ContentTypesMappers") - .find({ id: contentTypeId }) + .find({ id: contentTypeId,projectId: projectId }) .value(); await FieldMapperModel.read(); const fieldMappingData = contentTypeData.fieldMapping.map((itemId: any) => { const fieldData = FieldMapperModel.chain .get("field_mapper") - .find({ id: itemId }) + .find({ id: itemId, projectId: projectId }) .value(); return fieldData; }); @@ -492,6 +551,14 @@ const resetToInitialMapping = async (req: Request) => { ); } }; +/** + * Resets all the content types mapping for a specific project. + * + * @param projectId - The ID of the project. + * @returns The project details after resetting the content types mapping. + * @throws {BadRequestError} If the content mapper or project is not found. + * @throws {ExceptionFunction} If an error occurs while resetting the content types mapping. + */ const resetAllContentTypesMapping = async (projectId: string) => { const srcFunc = "resetAllContentTypesMapping"; @@ -524,7 +591,7 @@ const resetAllContentTypesMapping = async (projectId: string) => { const cData = contentMapperId.map((cId: any) => { const contentTypeData = ContentTypesMapperModelLowdb.chain .get("ContentTypesMappers") - .find({ id: cId }) + .find({ id: cId, projectId: projectId }) .value(); return contentTypeData; }); @@ -532,16 +599,16 @@ const resetAllContentTypesMapping = async (projectId: string) => { try { const contentTypes = cData; for (const contentType of contentTypes) { - if (contentType && !isEmpty(contentType.fieldMapping)) { + if (contentType && !isEmpty(contentType.fieldMapping)) { for (const field of contentType.fieldMapping) { await FieldMapperModel.read(); const fieldData = FieldMapperModel.chain .get("field_mapper") - .find({ id: field }) + .find({ id: field, projectId: projectId }) .value(); const fieldIndex = FieldMapperModel.chain .get("field_mapper") - .findIndex({ id: field }) + .findIndex({ id: field,projectId: projectId }) .value(); if (fieldIndex > -1) { @@ -560,7 +627,7 @@ const resetAllContentTypesMapping = async (projectId: string) => { if (!isEmpty(contentType?.id)) { const cIndex = ContentTypesMapperModelLowdb.chain .get("ContentTypesMappers") - .findIndex({ id: contentType?.id }) + .findIndex({ id: contentType?.id , projectId: projectId}) .value(); if (cIndex > -1) { await ContentTypesMapperModelLowdb.update((data: any) => { @@ -569,10 +636,8 @@ const resetAllContentTypesMapping = async (projectId: string) => { }); } } - } - return projectDetails; } catch (error: any) { logger.error( @@ -589,6 +654,13 @@ const resetAllContentTypesMapping = async (projectId: string) => { ); } }; +/** + * Removes the content mapping for a project. + * @param projectId - The ID of the project. + * @returns The project details after removing the content mapping. + * @throws {BadRequestError} If the project is not found. + * @throws {ExceptionFunction} If an error occurs while removing the content mapping. + */ const removeMapping = async (projectId: string) => { const srcFunc = "removeMapping"; await ProjectModelLowdb.read(); @@ -610,7 +682,7 @@ const removeMapping = async (projectId: string) => { const cData = projectDetails?.content_mapper.map((cId: any) => { const contentTypeData = ContentTypesMapperModelLowdb.chain .get("ContentTypesMappers") - .find({ id: cId }) + .find({ id: cId, projectId: projectId }) .value(); return contentTypeData; }); @@ -625,7 +697,7 @@ const removeMapping = async (projectId: string) => { await FieldMapperModel.read(); const fieldIndex = FieldMapperModel.chain .get("field_mapper") - .findIndex({ id: field }) + .findIndex({ id: field, projectId: projectId }) .value(); if (fieldIndex > -1) { await FieldMapperModel.update((fData: any) => { @@ -638,7 +710,7 @@ const removeMapping = async (projectId: string) => { if (!isEmpty(contentType?.id)) { const cIndex = ContentTypesMapperModelLowdb.chain .get("ContentTypesMappers") - .findIndex({ id: contentType?.id }) + .findIndex({ id: contentType?.id, projectId: projectId}) .value(); if (cIndex > -1) { await ContentTypesMapperModelLowdb.update((data: any) => { @@ -675,6 +747,11 @@ const removeMapping = async (projectId: string) => { ); } }; +/** + * Retrieves a single content type from the specified project. + * @param req - The request object containing the project ID, content type UID, and token payload. + * @returns An object containing the title, UID, and schema of the content type, or an error object if an error occurs. + */ const getSingleContentTypes = async (req: Request) => { const projectId = req?.params?.projectId; const contentTypeUID = req?.params?.contentTypeUid; @@ -716,6 +793,13 @@ const getSingleContentTypes = async (req: Request) => { schema: res?.data?.content_type?.schema, }; }; +/** + * Removes the content mapping for a project. + * @param req - The request object containing the project ID. + * @returns The project details after removing the content mapping. + * @throws {BadRequestError} If the project is not found. + * @throws {ExceptionFunction} If an error occurs while removing the content mapping. + */ const removeContentMapper = async (req: Request) => { const projectId = req?.params?.projectId; const srcFunc = "removeMapping"; @@ -738,7 +822,7 @@ const removeContentMapper = async (req: Request) => { const cData: ContentTypesMapper[] = projectDetails?.content_mapper.map((cId: string) => { const contentTypeData: ContentTypesMapper = ContentTypesMapperModelLowdb.chain .get("ContentTypesMappers") - .find({ id: cId }) + .find({ id: cId, projectId:projectId }) .value(); return contentTypeData; }); @@ -753,7 +837,7 @@ const removeContentMapper = async (req: Request) => { await FieldMapperModel.read(); const fieldIndex = FieldMapperModel.chain .get("field_mapper") - .findIndex({ id: field }) + .findIndex({ id: field, projectId:projectId }) .value(); if (fieldIndex > -1) { await FieldMapperModel.update((fData: any) => { @@ -766,7 +850,7 @@ const removeContentMapper = async (req: Request) => { if (!isEmpty(contentType?.id)) { const cIndex = ContentTypesMapperModelLowdb.chain .get("ContentTypesMappers") - .findIndex({ id: contentType?.id }) + .findIndex({ id: contentType?.id, projectId: projectId }) .value(); if (cIndex > -1) { await ContentTypesMapperModelLowdb.update((data: any) => { diff --git a/api/src/services/migration.service.ts b/api/src/services/migration.service.ts index 9c74fbf25..cdcad218b 100644 --- a/api/src/services/migration.service.ts +++ b/api/src/services/migration.service.ts @@ -9,6 +9,13 @@ import { HTTP_TEXTS, HTTP_CODES } from "../constants/index.js"; import { ExceptionFunction } from "../utils/custom-errors.utils.js"; import ProjectModelLowdb from "../models/project-lowdb.js"; +/** + * Creates a test stack. + * + * @param req - The request object containing the necessary parameters. + * @returns A promise that resolves to a LoginServiceType object. + * @throws ExceptionFunction if there is an error creating the stack. + */ const createTestStack = async (req: Request): Promise => { const srcFun = "createTestStack"; const orgId = req?.params?.orgId; @@ -99,6 +106,11 @@ const createTestStack = async (req: Request): Promise => { } }; +/** + * Deletes a test stack. + * @param req - The request object. + * @returns A promise that resolves to a LoginServiceType object. + */ const deleteTestStack = async (req: Request): Promise => { const srcFun = "deleteTestStack"; const projectId = req?.params?.projectId; diff --git a/api/src/services/org.service.ts b/api/src/services/org.service.ts index 90324fb39..1719ce781 100644 --- a/api/src/services/org.service.ts +++ b/api/src/services/org.service.ts @@ -9,6 +9,11 @@ import { HTTP_TEXTS, HTTP_CODES } from "../constants/index.js"; import { ExceptionFunction } from "../utils/custom-errors.utils.js"; import { BadRequestError } from "../utils/custom-errors.utils.js"; +/** + * Retrieves all stacks based on the provided request. + * @param req - The request object. + * @returns A promise that resolves to a LoginServiceType object. + */ const getAllStacks = async (req: Request): Promise => { const srcFun = "getAllStacks"; const orgId = req?.params?.orgId; @@ -82,6 +87,11 @@ const getAllStacks = async (req: Request): Promise => { } }; +/** + * Creates a stack. + * @param req - The request object. + * @returns A promise that resolves to a LoginServiceType object. + */ const createStack = async (req: Request): Promise => { const srcFun = "createStack"; const orgId = req?.params?.orgId; @@ -150,6 +160,12 @@ const createStack = async (req: Request): Promise => { } }; +/** + * Retrieves the locales from the CS API. + * @param req - The request object. + * @returns A promise that resolves to the response from the CS API. + * @throws {ExceptionFunction} If there is an error while retrieving the locales. + */ const getLocales = async (req: Request): Promise => { const srcFun = "getLocales"; const { token_payload } = req.body; @@ -204,6 +220,12 @@ const getLocales = async (req: Request): Promise => { } }; +/** + * Retrieves the status of a stack. + * @param req - The request object containing the orgId, token_payload, and stack_api_key. + * @returns An object containing the status and data of the stack. + * @throws ExceptionFunction if an error occurs while checking the status of the stack. + */ const getStackStatus = async (req: Request) => { const { orgId } = req.params; const { token_payload, stack_api_key } = req.body; @@ -286,15 +308,21 @@ const getStackStatus = async (req: Request) => { } }; //get all locals of particular stack +/** + * Retrieves stack local data. + * @param token_payload - The token payload. + * @param data - The data to process. + * @returns A promise that resolves with the stack local data. + */ const getStackLocal = async (token_payload: any, data: any) => { const srcFun = "getStackLocal"; - return new Promise(async (resolve, reject) => { + return new Promise(async (resolve) => { const authtoken = await getAuthtoken( token_payload?.region, token_payload?.user_id ); - let stacks = []; - for (let stack of data) { + const stacks = []; + for (const stack of data) { const [err, res] = await safePromise( https({ method: "GET", @@ -322,7 +350,7 @@ const getStackLocal = async (token_payload: any, data: any) => { status: err.response.status, }; } - let localesArr: any = []; + const localesArr: any = []; res?.data?.locales.map((lang: any) => { return localesArr.push({ code: lang.code, @@ -330,7 +358,7 @@ const getStackLocal = async (token_payload: any, data: any) => { }); }); - let obj = { + const obj = { name: stack.name, api_key: stack.api_key, master_locale: stack.master_locale, diff --git a/api/src/services/projects.service.ts b/api/src/services/projects.service.ts index f6cb13bb8..9e8ebfae0 100644 --- a/api/src/services/projects.service.ts +++ b/api/src/services/projects.service.ts @@ -20,15 +20,22 @@ import getAuthtoken from "../utils/auth.utils.js"; import https from "../utils/https.utils.js"; import getProjectUtil from "../utils/get-project.utils.js"; import logger from "../utils/logger.js"; -import { contentMapperService } from "./contentMapper.service.js"; +// import { contentMapperService } from "./contentMapper.service.js"; import { v4 as uuidv4 } from "uuid"; +/** + * Retrieves all projects based on the provided request object. + * + * @param req - The request object containing the orgId and token_payload. + * @returns A Promise that resolves to an array of projects. + * @throws {NotFoundError} If no projects are found. + */ const getAllProjects = async (req: Request) => { const orgId = req?.params?.orgId; const decodedToken = req.body.token_payload; - const { user_id = "", region = "" } = decodedToken; - + const { user_id = "", region = "" } = decodedToken; + await ProjectModelLowdb.read(); const projects = ProjectModelLowdb.chain .get("projects") @@ -45,6 +52,11 @@ const getAllProjects = async (req: Request) => { return projects; }; +/** + * Retrieves a project based on the provided request. + * @param req - The request object containing the orgId, projectId, and token_payload. + * @returns A Promise that resolves to the retrieved project. + */ const getProject = async (req: Request) => { const orgId = req?.params?.orgId; const projectId = req?.params?.projectId; @@ -65,6 +77,12 @@ const getProject = async (req: Request) => { return project; }; +/** + * Creates a new project. + * @param req - The request object containing the project details. + * @returns An object with the status, message, and project details. + * @throws ExceptionFunction if there is an error creating the project. + */ const createProject = async (req: Request) => { const orgId = req?.params?.orgId; const { name, description } = req.body; @@ -98,6 +116,8 @@ const createProject = async (req: Request) => { updated_at: new Date().toISOString(), created_at: new Date().toISOString(), isDeleted: false, + isNewStack: false, + newStackId: "", }; try { @@ -143,6 +163,12 @@ const createProject = async (req: Request) => { } }; +/** + * Updates a project based on the provided request. + * @param req - The request object containing the necessary information. + * @returns An object with the updated project details. + * @throws ExceptionFunction if an error occurs while updating the project. + */ const updateProject = async (req: Request) => { const orgId = req?.params?.orgId; const projectId = req?.params?.projectId; @@ -170,6 +196,13 @@ const updateProject = async (req: Request) => { ProjectModelLowdb.update((data: any) => { data.projects[projectIndex].name = updateData?.name; data.projects[projectIndex].description = updateData?.description; + if ( + data.projects[projectIndex].isNewStack === false && + updateData?.isNewStack === true + ) { + data.projects[projectIndex].isNewStack = updateData?.isNewStack; + data.projects[projectIndex].newStackId = updateData?.newStackId; + } data.projects[projectIndex].updated_by = user_id; data.projects[projectIndex].updated_at = new Date().toISOString(); project = data.projects[projectIndex]; @@ -211,6 +244,14 @@ const updateProject = async (req: Request) => { } }; +/** + * Updates the legacy CMS for a project. + * + * @param req - The request object containing the parameters and body. + * @returns An object with the status and data of the update operation. + * @throws BadRequestError if the project status is invalid. + * @throws ExceptionFunction if an error occurs during the update. + */ const updateLegacyCMS = async (req: Request) => { const { orgId, projectId } = req.params; const { token_payload, legacy_cms } = req.body; @@ -289,6 +330,12 @@ const updateLegacyCMS = async (req: Request) => { } }; +/** + * Updates the affix of a project. + * + * @param req - The request object containing the parameters and body. + * @returns An object with the status and data properties. + */ const updateAffix = async (req: Request) => { const srcFunc = "updateAffix"; const { orgId, projectId } = req.params; @@ -320,6 +367,12 @@ const updateAffix = async (req: Request) => { }; }; +/** + * Affixes the confirmation to a project in the database. + * + * @param req - The request object containing the parameters and body. + * @returns An object with the status and data properties. + */ const affixConfirmation = async (req: Request) => { const srcFunc = "affixConfirmation"; const { orgId, projectId } = req.params; @@ -352,9 +405,23 @@ const affixConfirmation = async (req: Request) => { }; }; +/** + * Updates the file format for a project. + * @param req - The request object containing the parameters and body. + * @returns An object with the status and data properties. + * @throws BadRequestError if the project status is invalid. + * @throws ExceptionFunction if an error occurs while updating the file format. + */ const updateFileFormat = async (req: Request) => { const { orgId, projectId } = req.params; - const { token_payload, file_format,file_path,is_localPath,is_fileValid,awsDetails } = req.body; + const { + token_payload, + file_format, + file_path, + is_localPath, + is_fileValid, + awsDetails, + } = req.body; const srcFunc = "updateFileFormat"; const projectIndex = (await getProjectUtil( projectId, @@ -441,6 +508,11 @@ const updateFileFormat = async (req: Request) => { } }; +/** + * Updates the file format confirmation for a project. + * @param req - The request object containing the parameters and body. + * @returns An object with the status and data properties. + */ const fileformatConfirmation = async (req: Request) => { const srcFunc = "fileformat"; const { orgId, projectId } = req.params; @@ -473,6 +545,14 @@ const fileformatConfirmation = async (req: Request) => { }; }; +/** + * Updates the destination stack for a project. + * + * @param req - The request object containing the parameters and body. + * @returns An object with the status and data of the update operation. + * @throws BadRequestError if the project status is invalid or the destination stack is not found. + * @throws ExceptionFunction if an error occurs while updating the destination stack. + */ const updateDestinationStack = async (req: Request) => { const { orgId, projectId } = req.params; const { token_payload, stack_api_key } = req.body; @@ -584,6 +664,13 @@ const updateDestinationStack = async (req: Request) => { } }; +/** + * Updates the current step of a project based on the provided request. + * @param req - The request object containing the parameters and body. + * @returns The updated project object. + * @throws {BadRequestError} If the current step cannot be updated. + * @throws {ExceptionFunction} If an error occurs while updating the current step. + */ const updateCurrentStep = async (req: Request) => { const { orgId, projectId } = req.params; const token_payload = req.body.token_payload; @@ -679,6 +766,11 @@ const updateCurrentStep = async (req: Request) => { } }; +/** + * Deletes a project. + * @param req - The request object containing the project ID and organization ID. + * @returns An object with the status and data properties. + */ const deleteProject = async (req: Request) => { const { orgId, projectId } = req.params; const decodedToken = req.body.token_payload; @@ -710,7 +802,7 @@ const deleteProject = async (req: Request) => { content_mapper_id.map((item: any) => { const contentMapperData = ContentTypesMapperModelLowdb.chain .get("ContentTypesMappers") - .find({ id: item }) + .find({ id: item, projectId:projectId }) .value(); const fieldMappingIds = contentMapperData?.fieldMapping; @@ -720,7 +812,7 @@ const deleteProject = async (req: Request) => { (fieldMappingIds || []).forEach((field: any) => { const fieldIndex = FieldMapperModel.chain .get("field_mapper") - .findIndex({ id: field }) + .findIndex({ id: field,projectId:projectId }) .value(); if (fieldIndex > -1) { FieldMapperModel.update((data: any) => { @@ -732,7 +824,7 @@ const deleteProject = async (req: Request) => { //delete all content Mapper which is related to Project const contentMapperID = ContentTypesMapperModelLowdb.chain .get("ContentTypesMappers") - .findIndex({ id: item }) + .findIndex({ id: item, projectId: projectId }) .value(); ContentTypesMapperModelLowdb.update((Cdata: any) => { delete Cdata.ContentTypesMappers[contentMapperID]; @@ -764,6 +856,13 @@ const deleteProject = async (req: Request) => { }; }; +/** + * Reverts a project by setting its 'isDeleted' property to false. + * + * @param req - The request object containing the parameters and body. + * @returns An object with the status and data of the reverted project. + * @throws {NotFoundError} If the project is not found. + */ const revertProject = async (req: Request) => { const { orgId, projectId } = req.params; const decodedToken = req.body.token_payload; @@ -806,6 +905,7 @@ const revertProject = async (req: Request) => { }; } }; + export const projectService = { getAllProjects, getProject, diff --git a/api/src/services/user.service.ts b/api/src/services/user.service.ts index 3d4d511a2..9c9a0467a 100644 --- a/api/src/services/user.service.ts +++ b/api/src/services/user.service.ts @@ -11,6 +11,13 @@ import AuthenticationModel from "../models/authentication.js"; import { safePromise, getLogMessage } from "../utils/index.js"; import logger from "../utils/logger.js"; +/** + * Retrieves the user profile based on the provided request. + * @param req - The request object containing the token payload. + * @returns A promise that resolves to the user profile. + * @throws {BadRequestError} If the user is not found. + * @throws {ExceptionFunction} If an error occurs while retrieving the user profile. + */ const getUserProfile = async (req: Request): Promise => { const srcFun = "getUserProfile"; const appTokenPayload: AppTokenPayload = req?.body?.token_payload; diff --git a/api/src/utils/async-router.utils.ts b/api/src/utils/async-router.utils.ts index 3ebb42336..712035973 100644 --- a/api/src/utils/async-router.utils.ts +++ b/api/src/utils/async-router.utils.ts @@ -1,5 +1,10 @@ import { Request, Response, NextFunction } from "express"; +/** + * Wraps an async function to handle errors and pass them to the Express error handler. + * @param fn - The async function to be wrapped. + * @returns A middleware function that handles async errors. + */ export const asyncRouter = (fn: any) => (req: Request, res: Response, next: NextFunction) => { Promise.resolve(fn(req, res, next)).catch(next); diff --git a/api/src/utils/auth.utils.ts b/api/src/utils/auth.utils.ts index 6ac51514a..30df20752 100644 --- a/api/src/utils/auth.utils.ts +++ b/api/src/utils/auth.utils.ts @@ -1,6 +1,13 @@ import AuthenticationModel from "../models/authentication.js"; import { UnauthorizedError } from "../utils/custom-errors.utils.js"; +/** + * Retrieves the authentication token for a given user in a specific region. + * @param region - The region of the user. + * @param userId - The ID of the user. + * @returns The authentication token for the user. + * @throws UnauthorizedError if the user is not found or the authentication token is missing. + */ export default async (region: string, userId: string) => { await AuthenticationModel.read(); const userIndex = AuthenticationModel.chain diff --git a/api/src/utils/custom-errors.utils.ts b/api/src/utils/custom-errors.utils.ts index 4b33d15f3..ee9204e84 100644 --- a/api/src/utils/custom-errors.utils.ts +++ b/api/src/utils/custom-errors.utils.ts @@ -1,55 +1,123 @@ import { HTTP_CODES, HTTP_TEXTS } from "../constants/index.js"; export class AppError extends Error { + /** + * Custom Error class for handling application errors. + */ constructor(public statusCode: number, message: string) { super(message); Object.setPrototypeOf(this, AppError.prototype); } } +/** + * Represents a custom error for a resource not found. + * Extends the base AppError class. + */ export class NotFoundError extends AppError { + /** + * Creates a new instance of NotFoundError. + * @param message - The error message. Defaults to "Not Found". + */ constructor(message: string = "Not Found") { super(HTTP_CODES.NOT_FOUND, message); } } +/** + * Represents a custom error for a bad request. + * Extends the base AppError class. + */ export class BadRequestError extends AppError { + /** + * Creates a new instance of the BadRequestError class. + * @param message The error message. + */ constructor(message: string = "Bad Request") { super(HTTP_CODES.BAD_REQUEST, message); } } +/** + * Represents a custom error related to database operations. + * Extends the base AppError class. + */ export class DatabaseError extends AppError { + /** + * Creates a new instance of the DatabaseError class. + * @param message The error message. + */ constructor(message: string = "DB error") { super(HTTP_CODES.SERVER_ERROR, message); } } +/** + * Represents a validation error that occurs during user validation. + * Extends the base AppError class. + */ export class ValidationError extends AppError { + /** + * Creates a new instance of the ValidationError class. + * @param message The error message associated with the validation error. Defaults to "User validation error". + */ constructor(message: string = "User validation error") { super(HTTP_CODES.UNPROCESSABLE_CONTENT, message); } } +/** + * Represents an Internal Server Error. + * This error is thrown when there is an internal server error in the application. + */ export class InternalServerError extends AppError { + /** + * Creates a new instance of the InternalServerError class. + * @param message The error message. + */ constructor(message: string = HTTP_TEXTS.INTERNAL_ERROR) { super(HTTP_CODES.SERVER_ERROR, message); } } +/** + * Represents an error that occurs when a user is unauthorized to access a resource. + * Extends the base AppError class. + */ export class UnauthorizedError extends AppError { + /** + * Creates a new instance of the UnauthorizedError class. + * @param message The error message. Defaults to the "Unauthorized" HTTP text. + */ constructor(message: string = HTTP_TEXTS.UNAUTHORIZED) { super(HTTP_CODES.UNAUTHORIZED, message); } } +/** + * Represents an error related to S3 operations. + * Extends the base AppError class. + */ export class S3Error extends AppError { + /** + * Creates a new instance of the S3Error class. + * @param message The error message. Defaults to HTTP_TEXTS.S3_ERROR. + */ constructor(message: string = HTTP_TEXTS.S3_ERROR) { super(HTTP_CODES.SERVER_ERROR, message); } } +/** + * Represents an ExceptionFunction class that extends the AppError class. + * This class is used to create custom exceptions with a specific HTTP status code. + */ export class ExceptionFunction extends AppError { + /** + * Creates a new instance of the ExceptionFunction class. + * @param message - The error message. + * @param httpStatus - The HTTP status code associated with the exception. + */ constructor(message: string, httpStatus: number) { super(httpStatus, message); } diff --git a/api/src/utils/get-project.utils.ts b/api/src/utils/get-project.utils.ts index 350cfd98c..cb32d8dc6 100644 --- a/api/src/utils/get-project.utils.ts +++ b/api/src/utils/get-project.utils.ts @@ -10,6 +10,16 @@ import { getLogMessage } from "../utils/index.js"; import logger from "./logger.js"; import { validate } from "uuid"; +/** + * Retrieves a project based on the provided project ID and query. + * @param projectId - The ID of the project to retrieve. + * @param query - The query to filter the projects. + * @param srcFunc - The source function name (optional). + * @param isIndex - Indicates whether to find the project by index (optional, default: false). + * @returns The retrieved project. + * @throws BadRequestError if the project ID is invalid or the project is not found. + * @throws ExceptionFunction if an error occurs during the retrieval process. + */ export default async ( projectId: string, query: MigrationQueryType, diff --git a/api/src/utils/https.utils.ts b/api/src/utils/https.utils.ts index 0ab6a892e..c878ee428 100644 --- a/api/src/utils/https.utils.ts +++ b/api/src/utils/https.utils.ts @@ -4,6 +4,9 @@ import { METHODS_TO_INCLUDE_DATA_IN_AXIOS, } from "../constants/index.js"; +/** + * Represents the HTTP request configuration. + */ type httpType = { url: string; method: string; @@ -11,6 +14,12 @@ type httpType = { data?: any; timeout?: number; }; +/** + * Sends an HTTP request using Axios. + * + * @param obj - The HTTP request object. + * @returns An object containing the response headers, status, and data. + */ export default async (obj: httpType) => { const { url, method, headers, data, timeout } = obj; const res = await axios(url, { diff --git a/api/src/utils/index.ts b/api/src/utils/index.ts index 40823eacc..15e6ea61d 100644 --- a/api/src/utils/index.ts +++ b/api/src/utils/index.ts @@ -1,17 +1,42 @@ +/** + * Throws an error with a custom message and status code. + * @param message - The error message. + * @param statusCode - The HTTP status code associated with the error. + * @throws {Error} - The error object with the specified message and status code. + */ export const throwError = (message: string, statusCode: number) => { throw Object.assign(new Error(message), { statusCode }); }; +/** + * Checks if a value is empty. + * @param val - The value to check. + * @returns `true` if the value is empty, `false` otherwise. + */ export const isEmpty = (val: unknown) => val === undefined || val === null || (typeof val === "object" && !Object.keys(val).length) || (typeof val === "string" && !val.trim().length); +/** + * Wraps a promise with error handling to ensure it always resolves with an array containing either an error or the result. + * @param promise - The promise to be wrapped. + * @returns A new promise that resolves with an array containing either an error or the result. + */ export const safePromise = (promise: Promise): Promise => promise.then((res) => [null, res]).catch((err) => [err]); //Generic method to get log message object +/** + * Returns a log message object. + * + * @param methodName - The name of the method. + * @param message - The log message. + * @param user - The user object. Defaults to an empty object. + * @param error - The error object. Optional. + * @returns The log message object. + */ export const getLogMessage = ( methodName: string, message: string, diff --git a/api/src/utils/jwt.utils.ts b/api/src/utils/jwt.utils.ts index d6f59aa7b..82e364bcd 100644 --- a/api/src/utils/jwt.utils.ts +++ b/api/src/utils/jwt.utils.ts @@ -4,6 +4,12 @@ import { AppTokenPayload } from "../models/types.js"; import { config } from "../config/index.js"; // @typescript-eslint/no-explicit-any +/** + * Generates a JWT token with the provided payload. + * + * @param payload - The payload to be included in the token. + * @returns The generated JWT token. + */ export const generateToken = (payload: AppTokenPayload): string => { return jwt.sign(payload, config.APP_TOKEN_KEY, { expiresIn: config.APP_TOKEN_EXP, diff --git a/api/src/utils/logger.ts b/api/src/utils/logger.ts index cbde4f7b0..b7baff0c3 100644 --- a/api/src/utils/logger.ts +++ b/api/src/utils/logger.ts @@ -1,6 +1,9 @@ import { createLogger, format, transports } from "winston"; //Logger for custom logs +/** + * The logger instance used for logging messages. + */ const logger = createLogger({ level: "info", format: format.combine(format.timestamp(), format.json()), diff --git a/api/src/utils/lowdb-lodash.utils.ts b/api/src/utils/lowdb-lodash.utils.ts index b87088a08..81bd511f8 100644 --- a/api/src/utils/lowdb-lodash.utils.ts +++ b/api/src/utils/lowdb-lodash.utils.ts @@ -1,6 +1,10 @@ import lodash from "lodash"; import { Low } from "lowdb"; +/** + * Represents a class that extends the Low class with Lodash functionality. + * @template T - The type of data stored in the LowWithLodash instance. + */ export default class LowWithLodash extends Low { chain: lodash.ExpChain = lodash.chain(this).get("data"); } diff --git a/api/src/validators/affix-confirmation.validator.ts b/api/src/validators/affix-confirmation.validator.ts index 5bbea21a7..837393dac 100644 --- a/api/src/validators/affix-confirmation.validator.ts +++ b/api/src/validators/affix-confirmation.validator.ts @@ -1,6 +1,11 @@ import { checkSchema } from "express-validator"; import { VALIDATION_ERRORS } from "../constants/index.js"; +/** + * Validates the affix_confirmation field in the request body. + * + * @returns {Object} The validation schema for the affix_confirmation field. + */ export default checkSchema({ affix_confirmation: { in: "body", diff --git a/api/src/validators/affix.validator.ts b/api/src/validators/affix.validator.ts index 62cf4af82..1dc70b5c0 100644 --- a/api/src/validators/affix.validator.ts +++ b/api/src/validators/affix.validator.ts @@ -1,6 +1,11 @@ import { checkSchema } from "express-validator"; import { VALIDATION_ERRORS, AFFIX_REGEX } from "../constants/index.js"; +/** + * Validates the 'affix' property in the request body. + * + * @returns {Object} The validation schema for the 'affix' property. + */ export default checkSchema({ affix: { in: "body", diff --git a/api/src/validators/auth.validator.ts b/api/src/validators/auth.validator.ts index 8ba097fe6..2148ae87d 100644 --- a/api/src/validators/auth.validator.ts +++ b/api/src/validators/auth.validator.ts @@ -1,7 +1,15 @@ import { checkSchema } from "express-validator"; import { VALIDATION_ERRORS, CS_REGIONS } from "../constants/index.js"; +/** + * Validates the authentication request body. + * + * @returns {Object} The validation schema for the authentication request body. + */ export default checkSchema({ + /** + * The email field in the request body. + */ email: { in: "body", isString: { @@ -22,6 +30,9 @@ export default checkSchema({ bail: true, }, }, + /** + * The password field in the request body. + */ password: { in: "body", isString: { @@ -30,6 +41,9 @@ export default checkSchema({ }, trim: true, }, + /** + * The region field in the request body. + */ region: { in: "body", isString: { @@ -43,6 +57,9 @@ export default checkSchema({ bail: true, }, }, + /** + * The 2FA token field in the request body. + */ tfa_token: { optional: true, in: "body", diff --git a/api/src/validators/cms.validator.ts b/api/src/validators/cms.validator.ts index 3b04f0625..024726b5e 100644 --- a/api/src/validators/cms.validator.ts +++ b/api/src/validators/cms.validator.ts @@ -1,6 +1,11 @@ import { checkSchema } from "express-validator"; import { VALIDATION_ERRORS } from "../constants/index.js"; +/** + * Validates the 'legacy_cms' field in the request body. + * + * @returns {Object} The validation schema for 'legacy_cms' field. + */ export default checkSchema({ legacy_cms: { in: "body", diff --git a/api/src/validators/destination-stack.validator.ts b/api/src/validators/destination-stack.validator.ts index 3c48f193c..e20439975 100644 --- a/api/src/validators/destination-stack.validator.ts +++ b/api/src/validators/destination-stack.validator.ts @@ -1,6 +1,11 @@ import { checkSchema } from "express-validator"; import { VALIDATION_ERRORS } from "../constants/index.js"; +/** + * Validates the destination stack API key. + * + * @returns {Object} The validation schema for the destination stack API key. + */ export default checkSchema({ stack_api_key: { in: "body", diff --git a/api/src/validators/file-format.validator.ts b/api/src/validators/file-format.validator.ts index 22aa06b1d..a8755511d 100644 --- a/api/src/validators/file-format.validator.ts +++ b/api/src/validators/file-format.validator.ts @@ -1,6 +1,11 @@ import { checkSchema } from "express-validator"; import { VALIDATION_ERRORS } from "../constants/index.js"; +/** + * Validates the file format. + * + * @returns {Object} The validation schema for the file format. + */ export default checkSchema({ file_format: { in: "body", diff --git a/api/src/validators/fileformat-confirmation.validator.ts b/api/src/validators/fileformat-confirmation.validator.ts index 199260966..4beb54b37 100644 --- a/api/src/validators/fileformat-confirmation.validator.ts +++ b/api/src/validators/fileformat-confirmation.validator.ts @@ -1,6 +1,11 @@ import { checkSchema } from "express-validator"; import { VALIDATION_ERRORS } from "../constants/index.js"; +/** + * Validates the file format confirmation field in the request body. + * + * @returns {Object} The validation schema for the file format confirmation field. + */ export default checkSchema({ fileformat_confirmation: { in: "body", diff --git a/api/src/validators/index.ts b/api/src/validators/index.ts index 269e5893e..1dd1e1549 100644 --- a/api/src/validators/index.ts +++ b/api/src/validators/index.ts @@ -10,6 +10,11 @@ import affixValidator from "./affix.validator.js"; import affixConfirmationValidator from "./affix-confirmation.validator.js"; import fileformatConfirmationValidator from "./fileformat-confirmation.validator.js"; +/** + * Middleware function that validates the request based on the specified route. + * @param route - The route to determine the validator to use. + * @returns The middleware function that performs the validation. + */ export default (route: string = "") => asyncRouter(async (req: Request, res: Response, next: NextFunction) => { const appValidators = { diff --git a/api/src/validators/project.validator.ts b/api/src/validators/project.validator.ts index d3e97973f..2df47ac1a 100644 --- a/api/src/validators/project.validator.ts +++ b/api/src/validators/project.validator.ts @@ -1,6 +1,11 @@ import { checkSchema } from "express-validator"; import { VALIDATION_ERRORS } from "../constants/index.js"; +/** + * Validates the project data. + * + * @returns {Object} The validation schema for the project data. + */ export default checkSchema({ name: { in: "body", diff --git a/ui/src/cmsData/legacyCms.json b/ui/src/cmsData/legacyCms.json index 08ef2bfd7..dacef21ef 100644 --- a/ui/src/cmsData/legacyCms.json +++ b/ui/src/cmsData/legacyCms.json @@ -338,7 +338,7 @@ "uid": "cs6c761d71844ac800" }, "title": "Enter Affix", - "description": "Please enter the keyword to be affixed to the source. Minimum 2 characters, maximum 5 characters and should not start with numbers and not include special characters", + "description": "Please enter the keyword to be affixed to the source. Minimum 2 characters, maximum 5 characters and should not start with numbers and not include special characters.", "step_lock_text": "Editing this step is currently disabled. To make changes in Draft mode, please deactivate the migration.", "lock": false, "active": false, diff --git a/ui/src/common/assets/icons.tsx b/ui/src/common/assets/icons.tsx index 0b7f5d6c0..84ff957fa 100644 --- a/ui/src/common/assets/icons.tsx +++ b/ui/src/common/assets/icons.tsx @@ -1,3 +1,11 @@ +/** + * SVG component for the "NO_PROJECTS_SEARCH" icon. + * + * @remarks + * This SVG represents an icon used for indicating no search results in a project. + * + * @returns The SVG component. + */ export const NO_PROJECTS_SEARCH = ( ); +/** + * This file contains the definition of the NO_PROJECTS SVG icon. + * The icon represents a graphic illustration of a folder with no projects. + * It is used as a visual representation when there are no projects available. + */ export const NO_PROJECTS = ( ); +/** + * SVG icon component for a caret pointing right. + * + * @component + * @name CARET_RIGHT + */ export const CARET_RIGHT = ( ); +/** + * SVG icon component for the trash icon. + * @constant + */ export const TRASH = ( ); +/** + * SVG icon for search. + */ export const SEARCH_ICON = ( ); -export const LOG_OUT = ( - - - - +/** + * SVG icon for logging out. + */ +export const LOG_OUT = ( + + + + -); +); +/** + * SVG icon component for the magnify icon. + * + * @remarks + * This component renders an SVG icon for the magnify icon. + * + * @returns The SVG icon component. + */ export const MAGNIFY = ( - - - - + + + + ); +/** + * SVG icon component for DEMAGNIFY. + * + * @remarks + * This component renders an SVG icon for the DEMAGNIFY action. + * + * @returns The SVG icon component. + */ export const DEMAGNIFY = ( - - - - + + + ); diff --git a/ui/src/components/AccountPage/accountPage.interface.ts b/ui/src/components/AccountPage/accountPage.interface.ts index 761991296..a9e11bcc8 100644 --- a/ui/src/components/AccountPage/accountPage.interface.ts +++ b/ui/src/components/AccountPage/accountPage.interface.ts @@ -1,5 +1,8 @@ import { LoginType } from '../../pages/Login/login.interface'; +/** + * Represents an account object. + */ export interface AccountObj { children: React.ReactNode; data: LoginType; diff --git a/ui/src/components/AccountPage/index.scss b/ui/src/components/AccountPage/index.scss index 9af38e4f2..2de759609 100644 --- a/ui/src/components/AccountPage/index.scss +++ b/ui/src/components/AccountPage/index.scss @@ -1,5 +1,11 @@ @import '../../scss/variables'; +/** + * Styles for the AccountPage component. + * + * The AccountPage component is a container component that represents the account page of the application. + * It contains styles for the layout, logo, background image, action section, content, heading, and circles. + */ .AccountPage { align-items: stretch; display: flex; @@ -153,6 +159,17 @@ } } +/** + * Styles for the copyright text in the AccountPage component. + * + * CSS properties: + * - bottom: 1.5rem; + * - color: $color-font-base; + * - font-size: 0.75rem; + * - letter-spacing: 0.02em; + * - left: 2.5rem; + * - position: absolute; + */ .copyright_text { bottom: 1.5rem; color: $color-font-base; diff --git a/ui/src/components/AccountPage/index.tsx b/ui/src/components/AccountPage/index.tsx index 4b1316670..8809a70b8 100644 --- a/ui/src/components/AccountPage/index.tsx +++ b/ui/src/components/AccountPage/index.tsx @@ -4,6 +4,12 @@ import { AccountObj } from './accountPage.interface'; // Styles import './index.scss'; +/** + * Renders the AccountPage component. + * + * @param {AccountObj} props - The props object containing data for the component. + * @returns {JSX.Element} The rendered AccountPage component. + */ const AccountPage = (props: AccountObj): JSX.Element => { const { heading, copyrightText } = props.data; diff --git a/ui/src/components/AdvancePropertise/advanceProperties.interface.ts b/ui/src/components/AdvancePropertise/advanceProperties.interface.ts index 6ce7ae7f6..3c5be0314 100644 --- a/ui/src/components/AdvancePropertise/advanceProperties.interface.ts +++ b/ui/src/components/AdvancePropertise/advanceProperties.interface.ts @@ -1,36 +1,151 @@ import { Advanced, FieldMapType } from '../ContentMapper/contentMapper.interface'; +/** + * Represents the properties of a schema. + */ export interface SchemaProps { + /** + * The type of the field. + */ fieldtype: string; + + /** + * The updated settings for the field. + */ value: UpdatedSettings; + + /** + * The ID of the row. + */ rowId: string; + + /** + * A function to update the field settings. + * @param rowId - The ID of the row. + * @param value - The advanced settings. + * @param checkBoxChanged - Indicates whether the checkbox has changed. + */ updateFieldSettings: (rowId: string, value: Advanced, checkBoxChanged: boolean) => void; + + /** + * Indicates whether the field is localized. + */ isLocalised: boolean; + + /** + * A function to close the modal. + */ closeModal: () => void; + + /** + * The data for the field map. + */ data: FieldMapType; + + /** + * The ID of the project. + */ projectId?: string; } +/** + * Represents the updated settings for a component. + */ export interface UpdatedSettings { + /** + * The minimum number of characters allowed. + */ MinChars?: string; + + /** + * The maximum number of characters allowed. + */ MaxChars?: number; + + /** + * The minimum range allowed. + */ MinRange?: number; + + /** + * The maximum range allowed. + */ MaxRange?: number; + + /** + * The minimum size allowed. + */ minSize?: string; + + /** + * The maximum size allowed. + */ maxSize?: number; + + /** + * The default value for the component. + */ DefaultValue?: string; + + /** + * The regular expression used for validation. + */ ValidationRegex?: string; + + /** + * The title of the component. + */ title?: string; + + /** + * The URL associated with the component. + */ url?: string; + + /** + * Indicates whether the component is mandatory. + */ Mandatory?: boolean; + + /** + * Indicates whether only images are allowed. + */ AllowImagesOnly?: boolean; + + /** + * Indicates whether the component is non-localizable. + */ NonLocalizable?: boolean; } +/** + * Represents the props for the AdvanceProperties component. + */ export interface Props { + /** + * The data for the component. + */ data: SchemaProps; + + /** + * The optional states for the component. + */ states?: StateType; + + /** + * The callback function for handling changes in the component. + * @param field - The field that was changed. + * @param event - The event object associated with the change. + * @param checkBoxChanged - Indicates whether the change was triggered by a checkbox. + */ handleChange?: (field: string, event: any, checkBoxChanged: boolean) => void; + + /** + * The callback function for handling toggles in the component. + * @param field - The field that was toggled. + * @param value - The new value of the toggle. + * @param checkBoxChanged - Indicates whether the toggle was triggered by a checkbox. + */ handleToggle?: (field: string, value: boolean, checkBoxChanged: boolean) => void; } diff --git a/ui/src/components/AdvancePropertise/index.scss b/ui/src/components/AdvancePropertise/index.scss index 212c06f8b..ab126ed6e 100644 --- a/ui/src/components/AdvancePropertise/index.scss +++ b/ui/src/components/AdvancePropertise/index.scss @@ -1,6 +1,15 @@ @import '../../scss/variables'; .options-class { + /** + * Styles for the AdvancePropertise component. + * + * This SCSS file contains styles for the AdvancePropertise component, which is used to define + * advanced properties for a UI component. The styles include flexbox properties for layout, + * margin, grid template columns, and gap. + * + * @module components/AdvancePropertise + */ display: flex; flex-direction: column; justify-content: space-between; @@ -8,14 +17,34 @@ margin-bottom: 20px; grid-template-columns: 1fr; gap: 20px; -} + } +/** + * Styles for the option label in the AdvancePropertise component. + * + * @class option-label + * @memberof components.AdvancePropertise + * @cssprop {margin-bottom} margin-bottom - The margin bottom value for the option label. + */ .option-label { margin-bottom: $px-10; } +/** + * Styles for the non-localizable-message class. + */ .non-localizable-message { margin-top: 0; margin-left: 30px; } +/** + * + * This SCSS file contains styles for the AdvancePropertise component. + * + * The styles define various classes for different elements within the component, + * such as radio fields, labels, info styles, options, notes, toggles, fields, + * separators, and select menus. + * + * The classes are used to apply specific styling to these elements in the component. + */ .modal-data { padding: 0.75rem; .radio-field { diff --git a/ui/src/components/AdvancePropertise/index.tsx b/ui/src/components/AdvancePropertise/index.tsx index 373737a52..060d9f937 100644 --- a/ui/src/components/AdvancePropertise/index.tsx +++ b/ui/src/components/AdvancePropertise/index.tsx @@ -22,7 +22,13 @@ import { ContentType } from '../ContentMapper/contentMapper.interface'; // Styles import './index.scss'; +/** + * Component for displaying advanced properties. + * @param props - The schema properties. + * @returns The rendered component. + */ const AdvancePropertise = (props: SchemaProps) => { + // State for toggle states const [toggleStates, setToggleStates] = useState({ minChars: props?.value?.MinChars, maxChars: props?.value?.MaxChars, @@ -41,6 +47,7 @@ const AdvancePropertise = (props: SchemaProps) => { embedAssests: true }); + // State for content types const [contentTypes, setContentTypes] = useState([]); const [ctValue, setCTValue] = useState(null); @@ -48,13 +55,22 @@ const AdvancePropertise = (props: SchemaProps) => { fetchContentTypes(''); }, []) - // Fetch content types list + /** + * Fetches the content types list. + * @param searchText - The search text. + */ const fetchContentTypes = async (searchText: string) => { const { data } = await getContentTypes(props?.projectId ?? '', 0, 10, searchText || ''); //org id will always present setContentTypes(data?.contentTypes); }; + /** + * Handles the change event for input fields. + * @param field - The field name. + * @param event - The change event. + * @param checkBoxChanged - Indicates if the checkbox was changed. + */ const handleOnChange = (field: string, event: React.ChangeEvent, checkBoxChanged: boolean) => { setToggleStates((prevStates) => ({ ...prevStates, @@ -76,6 +92,12 @@ const AdvancePropertise = (props: SchemaProps) => { ); }; + /** + * Handles the toggle change event. + * @param field - The field name. + * @param value - The new value. + * @param checkBoxChanged - Indicates if the checkbox was changed. + */ const handleToggleChange = (field: string, value: boolean, checkBoxChanged: boolean) => { setToggleStates((prevStates) => ({ ...prevStates, @@ -97,13 +119,14 @@ const AdvancePropertise = (props: SchemaProps) => { ); }; + // Option for content types const option = Array.isArray(contentTypes) ? contentTypes.map((option) => ({ label: option?.otherCmsTitle, value: option?.otherCmsTitle })) : [{ label: contentTypes, value: contentTypes }]; return ( <> - +
{(props?.fieldtype === 'Single Line Textbox' || props?.fieldtype === 'Multi Line Textbox') && ( diff --git a/ui/src/components/Card/card.interface.ts b/ui/src/components/Card/card.interface.ts index 74c43df14..fe72a87b3 100644 --- a/ui/src/components/Card/card.interface.ts +++ b/ui/src/components/Card/card.interface.ts @@ -1,5 +1,8 @@ import { ProjectsObj } from '../../pages/Projects/projects.interface'; +/** + * Represents the project type. + */ export interface ProjectType { project?: ProjectsObj; } diff --git a/ui/src/components/Card/card.scss b/ui/src/components/Card/card.scss index 8a3917eaa..019a9875f 100644 --- a/ui/src/components/Card/card.scss +++ b/ui/src/components/Card/card.scss @@ -1,5 +1,8 @@ @import '../../scss/variables'; +/** + * Styles for the ProjectCard component. + */ .ProjectCard { background: $color-brand-white-base; border-radius: 0.6rem; @@ -7,14 +10,26 @@ transition: box-shadow 0.3s ease; width: 20rem; } + +/** + * Styles for the wrapper of the ProjectCard component. + */ .ProjectCardWrapper { padding: 18px; } + +/** + * Styles for the heading of the ProjectCard component. + */ .ProjectCard__heading { display: flex; margin-bottom: $px-20; padding: 0; } + +/** + * Styles for the title of the ProjectCard component. + */ .ProjectCard__title { color: $color-font-base; display: -webkit-box; @@ -28,6 +43,10 @@ -webkit-line-clamp: 2; -webkit-box-orient: vertical; } + +/** + * Styles for the footer of the ProjectCard component. + */ .ProjectCard__footer { display: flex; position: relative; @@ -39,27 +58,44 @@ padding: 0.9375rem 1.875rem; align-items: center; } + +/** + * Styles for the content of the ProjectCard component. + */ .ProjectCard__content { // margin: 0 1rem 2rem; padding: 0; text-align: center; } + +/** + * Styles for the status unit of the ProjectCard component. + */ .ProjectCard__Staus-unit { padding: 0 1em; } + +/** + * Styles for the unit of the ProjectCard component. + */ .ProjectCard__unit { border-left: 1px solid $color-brand-secondary-lightest; padding: 0 1em; } -.ProjectCard__unit:not(:last-child) { - border-right: 1px solid $color-base-gray-40; -} + +/** + * Styles for the stats of the ProjectCard component. + */ .ProjectCard__stats { display: flex; flex-wrap: nowrap; justify-content: center; padding-bottom: $px-10; } + +/** + * Styles for the stats category of the ProjectCard component. + */ .ProjectCard__stats-category { border: 1px solid $color-brand-secondary-lightest; border-radius: 8px; @@ -68,31 +104,56 @@ color: $color-font-base; padding: 4px 8px; text-transform: capitalize; - svg { - margin-right: 5px; - } - &.draft { - background-color: $color-base-white-10; - } - &.completed { - background-color: $color-brand-success-light; - } - &.failed { - background-color: $color-brand-fail-light; - } - &.pending { - background-color: $color-brand-attention-light; - } } + +/** + * Styles for the draft stats category of the ProjectCard component. + */ +.ProjectCard__stats-category.draft { + background-color: $color-base-white-10; +} + +/** + * Styles for the completed stats category of the ProjectCard component. + */ +.ProjectCard__stats-category.completed { + background-color: $color-brand-success-light; +} + +/** + * Styles for the failed stats category of the ProjectCard component. + */ +.ProjectCard__stats-category.failed { + background-color: $color-brand-fail-light; +} + +/** + * Styles for the pending stats category of the ProjectCard component. + */ +.ProjectCard__stats-category.pending { + background-color: $color-brand-attention-light; +} + +/** + * Styles for the stats title of the ProjectCard component. + */ .ProjectCard__stats-Title { color: $color-stepper-title; display: block; font-size: $size-font-small; margin-bottom: $space-4; } + +/** + * Styles for the validation color of the ProjectCard component. + */ .validation-color { color: $color-brand-warning-medium; } + +/** + * Styles for the CMS of the ProjectCard component. + */ .ProjectCard__cms { border: 1px solid $color-brand-secondary-lightest; border-radius: 8px; @@ -101,6 +162,10 @@ padding: 4px 8px; text-transform: capitalize; } + +/** + * Styles for the modified date of the ProjectCard component. + */ .ProjectCard__modified-date { color: $color-stepper-title; display: block; diff --git a/ui/src/components/Card/index.tsx b/ui/src/components/Card/index.tsx index 44bc169f1..ffd010899 100644 --- a/ui/src/components/Card/index.tsx +++ b/ui/src/components/Card/index.tsx @@ -12,16 +12,26 @@ import { getProject } from '../../services/api/project.service'; import './card.scss'; import { RootState } from '../../store'; +/** + * Renders a card component for a project in a list. + * @param project - The project object containing project details. + */ const CardList = ({ project }: ProjectType) => { const navigate = useNavigate(); - const selectedOrganisation = useSelector((state:RootState)=>state?.authentication?.selectedOrganisation); + const selectedOrganisation = useSelector((state: RootState) => state?.authentication?.selectedOrganisation); const [projectDetails, setprojectDetails] = useState(''); + /** + * Handles the click event when a project card is clicked. + * Navigates to the project migration steps page. + * @param id - The ID of the project. + */ const onClickProject = (id: string) => { if (isEmptyString(id)) return; navigate(`/projects/${id}/migration/steps/1`); }; + const iconMapping: { [key: string]: string } = { '0': 'Information', '1': 'Warning', @@ -31,6 +41,7 @@ const CardList = ({ project }: ProjectType) => { '5': 'CheckCircleDark', '6': 'Close', }; + const statusClassMapping: { [key: string]: string } = { '0': 'draft', '1': 'pending', @@ -40,6 +51,7 @@ const CardList = ({ project }: ProjectType) => { '5': 'completed', '6': 'failed', }; + const status = project?.status ?? '0'; const statusClass = statusClassMapping[status] || ''; const icon = iconMapping[status] || ''; @@ -56,7 +68,7 @@ const CardList = ({ project }: ProjectType) => { }; fetchProject(); }, [selectedOrganisation?.value, project?.id]); - + return (
onClickProject(project?.id || '')}> @@ -66,7 +78,7 @@ const CardList = ({ project }: ProjectType) => {
{project?.name &&

{project?.name}

}
- +
diff --git a/ui/src/components/Common/AddStack/addStack.interface.ts b/ui/src/components/Common/AddStack/addStack.interface.ts index da16ddc96..944c38fab 100644 --- a/ui/src/components/Common/AddStack/addStack.interface.ts +++ b/ui/src/components/Common/AddStack/addStack.interface.ts @@ -1,70 +1,199 @@ import { IDropDown } from '../../../context/app/app.interface'; +/** + * Represents the data structure for adding a stack in CMS. + */ export interface AddStackCMSData { + /** + * The primary call-to-action for the stack. + */ primary_cta: PrimaryCta; + + /** + * The secondary call-to-action for the stack. + */ secondary_cta: SecondaryCta; + + /** + * The description of the stack. + */ stack_description: string; + + /** + * The placeholder text for the stack description. + */ stack_description_placeholder: string; + + /** + * The localized description of the stack. + */ stack_locale_description: string; + + /** + * The locales supported by the stack. + */ stack_locales: string; + + /** + * The name of the stack. + */ stack_name: string; + + /** + * The placeholder text for the stack name. + */ stack_name_placeholder: string; + + /** + * The title of the stack. + */ title: string; } +/** + * Represents the primary call-to-action button for adding a stack. + */ export interface PrimaryCta { title: string; } +/** + * Represents the Secondary CTA interface. + */ export interface SecondaryCta { title: string; } +/** + * Interface representing the default data for AddStackCMSData. + */ export const defaultAddStackCMSData: AddStackCMSData = { + /** + * The title of the primary call-to-action button. + */ primary_cta: { title: '' }, + /** + * The title of the secondary call-to-action button. + */ secondary_cta: { title: '' }, + /** + * The description of the stack. + */ stack_description: '', + /** + * The placeholder for the stack description input field. + */ stack_description_placeholder: '', + /** + * The localized description of the stack. + */ stack_locale_description: '', + /** + * The locales supported by the stack. + */ stack_locales: '', + /** + * The name of the stack. + */ stack_name: '', + /** + * The placeholder for the stack name input field. + */ stack_name_placeholder: '', + /** + * The title of the stack. + */ title: '' }; +/** + * Represents the props for the AddStack component. + */ export interface AddStackProps { + /** + * The default values for the stack. + */ defaultValues: Stack; + + /** + * The locales for the dropdown. + */ locales: IDropDown[]; - onSubmit: (value: Stack) => {}; + + /** + * The function to be called when the form is submitted. + * @param value - The submitted stack value. + */ + onSubmit: (value: Stack) => void; + + /** + * The selected organisation. + */ selectedOrganisation: string; + + /** + * The function to close the modal. + */ closeModal: () => void; } +/** + * Represents a stack. + */ export interface Stack { + /** + * The name of the stack. + */ name: string; + + /** + * The description of the stack. + */ description: string; + + /** + * The locale of the stack. + */ locale: string; } +/** + * Represents the data structure for a stack. + */ export interface StackData { name: string; description: string; locale: Locale; } +/** + * Represents a locale. + */ interface Locale { value: string; } +/** + * Represents the response object returned by the server. + */ export interface Response { data: Data; } +/** + * Represents the data structure for the 'Data' interface. + */ interface Data { locales: LocaleType; } +/** + * Represents a type that defines a mapping of string keys to string values. + */ interface LocaleType { [key: string]: string; } +/** + * Represents the errors that can occur when adding a stack. + */ export interface Errors { name: string; locale: string; diff --git a/ui/src/components/Common/AddStack/addStack.scss b/ui/src/components/Common/AddStack/addStack.scss index 5a6647c80..d768305c2 100644 --- a/ui/src/components/Common/AddStack/addStack.scss +++ b/ui/src/components/Common/AddStack/addStack.scss @@ -1,11 +1,28 @@ +/** + * Styles for the ReactModal__add-stack class. + */ .ReactModal__add-stack { + /** + * Styles for the textarea inside the Description-field class. + * Disables the resizing of the textarea. + */ .Description-field > textarea { resize: none; } + + /** + * Styles for the single-value inside the Select__single-value class. + * Overrides the width to 200px. + */ .Select__single-value { width: 200px !important; } } + +/** + * Styles for the ReactModal__Overlay, ReactModal__Content, and ReactModal__Content__body.selectWrapperBody classes. + * Sets the overflow property to visible. + */ .ReactModal__Overlay .ReactModal__Content .ReactModal__Content__body.selectWrapperBody { overflow: visible; } diff --git a/ui/src/components/Common/AddStack/addStack.tsx b/ui/src/components/Common/AddStack/addStack.tsx index a8dc2ef24..9a6605c50 100644 --- a/ui/src/components/Common/AddStack/addStack.tsx +++ b/ui/src/components/Common/AddStack/addStack.tsx @@ -37,11 +37,22 @@ export interface Stack { locale: string; } +/** + * Renders the AddStack component. + * @param props - The component props. + * @returns The JSX element representing the AddStack component. + */ const AddStack = (props: any): JSX.Element => { + // State variables const [isProcessing, setIsProcessing] = useState(false); const [isLoading, setIsLoading] = useState(true); const [allLocales, setAllLocales] = useState([]); const [addStackCMSData, setAddStackCMSData] = useState(defaultAddStackCMSData); + + /** + * Handles the form submission. + * @param formData - The form data. + */ const onSubmit = async (formData: any) => { setIsProcessing(true); const resp = await props?.onSubmit({ @@ -63,10 +74,10 @@ const AddStack = (props: any): JSX.Element => { }; useEffect(() => { - //check if offline CMS data field is set to true, if then read data from cms data file. + // Check if offline CMS data field is set to true, if then read data from cms data file. getCMSDataFromFile(CS_ENTRIES?.ADD_STACK) .then((data: AddStackCMSData) => { - //Check for null + // Check for null if (!data) { setAddStackCMSData(defaultAddStackCMSData); setIsLoading(false); @@ -81,7 +92,7 @@ const AddStack = (props: any): JSX.Element => { setIsLoading(false); }); - //fetch all locales + // Fetch all locales getAllLocales(props?.selectedOrganisation) .then((response: any) => { const rawMappedLocalesMapped = @@ -100,7 +111,6 @@ const AddStack = (props: any): JSX.Element => { .catch((err: any) => { console.error(err); }); - //org id will always be there window.addEventListener('popstate', props?.closeModal); @@ -121,7 +131,7 @@ const AddStack = (props: any): JSX.Element => { { + validate={(values: any) => { const errors: any = {}; if (!values?.name || values?.name?.trim().length < 1) { errors.name = 'Stack name required'; @@ -213,13 +223,13 @@ const AddStack = (props: any): JSX.Element => { error={(meta?.error || meta?.submitError) && meta?.touched} /> {meta?.error && meta?.touched && ( - - {meta?.error} - - )} + + {meta?.error} + + )}
); diff --git a/ui/src/components/Common/Card/card.interface.ts b/ui/src/components/Common/Card/card.interface.ts index c48478488..db2068ec0 100644 --- a/ui/src/components/Common/Card/card.interface.ts +++ b/ui/src/components/Common/Card/card.interface.ts @@ -1,11 +1,36 @@ +/** + * Represents the interface for a card type. + */ export interface ICardType { + /** + * The title of the card. + */ title: string; + + /** + * The group name of the card. + */ group_name?: string; + + /** + * The description of the card. + */ description?: string; + + /** + * The CMS ID of the card. + */ cms_id?: string; + + /** + * The file format ID of the card. + */ fileformat_id?: string; } +/** + * Represents the default card type. + */ export const defaultCardType: ICardType = { title: '', group_name: '', diff --git a/ui/src/components/Common/Card/card.scss b/ui/src/components/Common/Card/card.scss index 6d90f2822..8b85919de 100644 --- a/ui/src/components/Common/Card/card.scss +++ b/ui/src/components/Common/Card/card.scss @@ -1,5 +1,11 @@ @import '../../../scss/variables'; +/** + * Styles for the service list container. + * + * @class service_list + * @memberof components.Common.Card + */ .service_list { text-align: center; display: flex; @@ -9,10 +15,18 @@ margin-bottom: 10px; } +/** + * Styles for the trigger_list class. + * This class sets the minimum width of 120px for the element. + */ .trigger_list { min-width: 120px !important; } +/** + * Styles for the connector list in the card component. + * Overrides the default styles of the tippy-box-light class. + */ .connector_list .tippy-box-light { max-width: $px-360 !important; padding: $px-15 !important; @@ -21,6 +35,9 @@ font-weight: $font-weight-regular; } +/** + * Styles for the connector list, action list, and trigger list components. + */ .connector_list, .action_list, .trigger_list { @@ -35,19 +52,61 @@ gap: 12px; } +/** + * Styles for the automation connectors help text within the connector list. + * + * Selectors: + * - .connector_list: The parent container for the connector list. + * - .tippy-box-light: The tooltip box with light theme. + * - #automationConnectorsHelpText: The help text element with the ID "automationConnectorsHelpText". + * + * CSS Properties: + * - cursor: Sets the cursor type to default. + * - color: Sets the text color to the base gray-20 color variable. + */ .connector_list .tippy-box-light #automationConnectorsHelpText { cursor: default; color: $color-base-gray-20; } +/** + * Applies a box shadow effect to the .connector_list element when hovered. + * + * @hover + * @cssproperty box-shadow - The box shadow effect to apply. + * @cssvalue 0 3px $px-5 $px-2 rgb(215 215 215) - The specific box shadow values. + */ .connector_list:hover { box-shadow: 0 3px $px-5 $px-2 rgb(215 215 215); } +/** + * Styles for the service icon in the card component. + * + * @class service_icon + * @memberof components.Common.Card + * + * @cssprop {padding} padding - The padding of the service icon. + */ .service_icon { padding: $space-20 0; } +/** + * Styles for the service title in the card component. + * + * @class .service_title + * @memberof components.Common.Card + * + * @cssprop {padding} padding - The padding of the service title. + * @cssprop {min-height} min-height - The minimum height of the service title. + * @cssprop {max-height} max-height - The maximum height of the service title. + * @cssprop {overflow-y} overflow-y - The overflow behavior for the service title. + * @cssprop {font-weight} font-weight - The font weight of the service title. + * @cssprop {font-size} font-size - The font size of the service title. + * @cssprop {border-radius} border-radius - The border radius of the service title. + * @cssprop {background} background - The background color of the service title. + */ .service_title { padding: $px-5 $px-5; min-height: $px-40; @@ -59,6 +118,19 @@ background: $color-base-white-10; } +/** + * Styles for the header of the full-screen page layout. + * + * @class .PageLayout--full-screen .PageLayout__head + * @memberof components.Common.Card + * + * @cssprop {position} position - The position of the header. + * @cssprop {border} border-bottom - The bottom border of the header. + * @cssprop {box-shadow} box-shadow - The shadow of the header. + * @cssprop {border} border - The border of the header. + * @cssprop {margin} margin-top - The top margin of the header. + * @cssprop {z-index} z-index - The stacking order of the header. + */ .PageLayout--full-screen .PageLayout__head { position: relative; border-bottom: 1px solid rgb(226, 235, 248); @@ -67,6 +139,13 @@ margin-top: 3.5rem; z-index: 8; } +/** + * CSS class for a centered card title. + * Adds a margin of 10px to the title. + */ +.centered-card-title { + margin: 10px; +} .centered-card-title{ margin: 10px; } diff --git a/ui/src/components/Common/Card/card.tsx b/ui/src/components/Common/Card/card.tsx index fc0a9b7e1..2d2d081fe 100644 --- a/ui/src/components/Common/Card/card.tsx +++ b/ui/src/components/Common/Card/card.tsx @@ -5,6 +5,9 @@ import { addDomainInPath } from '../../../utilities/functions'; import './card.scss'; +/** + * Props for the Card component. + */ type CardProps = { data: any; idField?: string; @@ -13,6 +16,15 @@ type CardProps = { cardType?: string; }; +/** + * Renders a card component. + * + * @param data - The data object for the card. + * @param selectedCard - The currently selected card. + * @param onCardClick - The callback function to handle card click event. + * @param cardType - The type of the card. + * @param idField - The field name for the card's ID. Defaults to 'id'. + */ const Card = ({ data, selectedCard, onCardClick, cardType, idField = 'id' }: CardProps) => { const imgStyle = { width: cardType === 'legacyCMS' ? '60px' : '46px', diff --git a/ui/src/components/Common/DeleteProjectModal/index.tsx b/ui/src/components/Common/DeleteProjectModal/index.tsx index d5abbf786..b175dfadb 100644 --- a/ui/src/components/Common/DeleteProjectModal/index.tsx +++ b/ui/src/components/Common/DeleteProjectModal/index.tsx @@ -15,6 +15,12 @@ import { deleteProject } from '../../../services/api/project.service'; // Interfaces import { SettingsModalProps } from '../../../components/Modal/modal.interface'; +/** + * Renders a modal component for deleting a project. + * + * @param {SettingsModalProps} props - The component props. + * @returns {JSX.Element} The rendered DeleteProjectModal component. + */ const DeleteProjectModal = (props: SettingsModalProps) => { const { closeModal, @@ -24,7 +30,13 @@ const DeleteProjectModal = (props: SettingsModalProps) => { selectedOrg } = props; - const handleDeleteProject = async (closeModal: () => void) => { + /** + * Handles the deletion of the project. + * + * @param {() => void} closeModal - A function to close the modal. + * @returns {Promise} A promise that resolves when the project is deleted. + */ + const handleDeleteProject = async (closeModal: () => void): Promise => { const response = await deleteProject(selectedOrg?.value || '', projectId ?? '',); if (response?.status === 200) { @@ -44,6 +56,7 @@ const DeleteProjectModal = (props: SettingsModalProps) => { }, 1200) } } + return ( <> ) => void; } +/** + * Renders a component that displays a link and a checkbox. + * + * @component + * @example + * // Usage: + * { + * console.log('Checkbox changed:', e.target.checked); + * }} + * /> + * + * @param {Object} props - The component props. + * @param {Object} props.cta - The link details. + * @param {string} props.cta.href - The URL of the link. + * @param {string} props.cta.title - The title of the link. + * @param {boolean} props.isCheckedBoxChecked - Indicates whether the checkbox is checked. + * @param {boolean} props.isDisable - Indicates whether the component is disabled. + * @param {string} props.label - The label for the checkbox. + * @param {boolean} props.isLabelFullWidth - Indicates whether the label should take full width. + * @param {function} props.onChange - The event handler for the checkbox change event. + * @returns {JSX.Element} The rendered component. + */ const DocLink = ({ cta, isCheckedBoxChecked, diff --git a/ui/src/components/Common/FileUpload/fileupload.scss b/ui/src/components/Common/FileUpload/fileupload.scss index 6a2e32bda..44476f91f 100644 --- a/ui/src/components/Common/FileUpload/fileupload.scss +++ b/ui/src/components/Common/FileUpload/fileupload.scss @@ -1,4 +1,7 @@ @import '../../../scss/variables'; +/** + * Styles for the file upload container. + */ .file-upload-container { display: flex; height: 21.25rem; @@ -10,6 +13,9 @@ border-radius: 0.25rem; border: 0.125rem dashed $color-base-gray-40; + /** + * Styles for the file upload element. + */ .file-upload { display: flex; height: 4.5rem; @@ -20,6 +26,9 @@ flex-shrink: 0; } + /** + * Styles for the text inside the file upload element. + */ .file-upload-text { text-align: center; font-family: Inter; @@ -30,6 +39,9 @@ letter-spacing: 0.16px; } + /** + * Styles for the image inside the file upload container. + */ .file-upload-image { width: 15rem; height: 15rem; @@ -37,14 +49,32 @@ } } +/** + * Adds a hover effect to the element, displaying a dashed border in the base purple color. + * + * @class hover + */ .hover { border: 0.125rem dashed $color-base-purple; } +/** + * This SCSS file contains styles for the FileUpload component. + */ + .hidden { display: none; } +/** + * Styles for the validation container component. + * + * The validation container is a flex container that displays a validation message + * for a file upload component. It has a white background color, a fixed height, + * and a border. The container is centered vertically and has no padding. + * + * @class validation-container + */ .validation-container { display: flex; background-color: $color-base-white-5; diff --git a/ui/src/components/Common/FileUpload/index.tsx b/ui/src/components/Common/FileUpload/index.tsx index 1945e9955..73958cd4f 100644 --- a/ui/src/components/Common/FileUpload/index.tsx +++ b/ui/src/components/Common/FileUpload/index.tsx @@ -11,16 +11,30 @@ type FileUploadProps = { projectId: string; }; +/** + * FileUpload component for uploading files. + * + * @param {FileUploadProps} props - The props for the FileUpload component. + * @returns {JSX.Element} The rendered FileUpload component. + */ const FileUpload = (props: FileUploadProps) => { const [isDragOver, setIsDragOver] = useState(false); const fileInputRef = useRef(null); const targetRef = useRef(null); + /** + * Empty method to handle setting upload modal to false. + */ const handleSetUploadModalFalse = (): any => { // Empty Method }; + /** + * Reads the files and opens the upload modal. + * + * @param {FileList | null} filesList - The list of files to be uploaded. + */ const readFiles = (filesList: FileList | null) => { //handle Null check if (!filesList) return; @@ -49,6 +63,11 @@ const FileUpload = (props: FileUploadProps) => { setIsDragOver(false); }; + /** + * Handles the drop event when files are dropped onto the component. + * + * @param {DragEvent} e - The drop event. + */ const handleDrop = (e: DragEvent) => { e.preventDefault(); e.stopPropagation(); @@ -57,22 +76,43 @@ const FileUpload = (props: FileUploadProps) => { e.dataTransfer.clearData(); }; + /** + * Handles the drag toggle event when dragging files over the component. + * + * @param {boolean} flag - The flag indicating whether the files are being dragged over the component. + * @returns {Function} The event handler function. + */ const handleDragToggle = (flag: boolean) => (e: DragEvent) => { e.preventDefault(); setIsDragOver(flag); }; + /** + * Handles the file change event when a file is selected using the file input. + * + * @param {ChangeEvent} e - The file change event. + */ const handleFileChange = (e: ChangeEvent) => { e.preventDefault(); readFiles(e?.target?.files); }; + /** + * Handles the file select event when the "Choose file" link is clicked. + * + * @param {any} e - The file select event. + */ const handleFileSelect = (e: any) => { e.preventDefault(); fileInputRef?.current?.click(); }; + /** + * Handles the onClose event after the files are uploaded. + * + * @param {IFile[]} files - The uploaded files. + */ const onCloseAfterUpload = (files: IFile[]) => { props.handleOnFileUpload(files); }; diff --git a/ui/src/components/Common/FileUpload/upload.scss b/ui/src/components/Common/FileUpload/upload.scss index b6b680fb9..13ab47b9c 100644 --- a/ui/src/components/Common/FileUpload/upload.scss +++ b/ui/src/components/Common/FileUpload/upload.scss @@ -1,11 +1,21 @@ @import '../../../scss/variables'; /* Asset upload window css */ +/** + * Styles for the asset upload component. + * + * @class .asset-upload + */ .asset-upload { background: $color-brand-white-base; box-shadow: 0 $space-4 $space-30 rgba(0, 0, 0, 0.25); width: 31.25rem; + /** + * Styles for the heading section of the asset upload component. + * + * @class .asset-upload__heading + */ &__heading { align-items: center; border-radius: $radii-4; @@ -14,26 +24,58 @@ justify-content: space-between; padding: 0 calc(var(--space-22) + 0.063rem); + /** + * Styles for the in-progress state of the heading section. + * + * @class .in-progress + */ &.in-progress { background-color: $color-brand-primary-base; } + /** + * Styles for the success state of the heading section. + * + * @class .success + */ &.success { background-color: $color-brand-success-base; } + /** + * Styles for the attention state of the heading section. + * + * @class .attention + */ &.attention { background-color: $color-brand-attention-base; } + /** + * Styles for the warning state of the heading section. + * + * @class .warning + */ &.warning { background-color: #ffab00 !important; } + /** + * Styles for the actions section of the heading. + * + * @class .asset-upload-actions + */ .asset-upload-actions { justify-content: space-between; width: 4.375rem; + /** + * Styles for the cancel, minimize, and maximize buttons in the actions section. + * + * @class .asset-upload__cancel + * @class .asset-upload__minimize + * @class .asset-upload__maximize + */ &__cancel, &__minimize, &__maximize { @@ -45,20 +87,34 @@ justify-content: center; width: $space-30; + /** + * Styles for the hover state of the cancel, minimize, and maximize buttons. + */ &:hover { background: rgba(34, 34, 34, 0.2); } + /** + * Styles for the SVG icons in the cancel, minimize, and maximize buttons. + */ & > svg { cursor: pointer; } } + /** + * Styles for the SVG icons in the actions section. + */ & > svg { cursor: pointer; } } + /** + * Styles for the count section in the heading. + * + * @class .asset-upload__count + */ .asset-upload__count { color: $color-font-white; font-size: $size-font-xl; @@ -67,17 +123,32 @@ } } + /** + * Styles for the body section of the asset upload component. + * + * @class .asset-upload__body + */ &__body { height: 19.06rem; margin-right: $space-15; overflow-y: auto; + /** + * Styles for each file in the body section. + * + * @class .asset-upload__file + */ .asset-upload__file { display: flex; height: 3.75rem; justify-content: space-between; padding: $space-15; + /** + * Styles for the name section of each file. + * + * @class .asset-upload__name + */ &__name { color: $color-font-black; font-size: $size-font-medium; @@ -87,17 +158,37 @@ // @include text-wrapper; } + /** + * Styles for the actions section of each file. + * + * @class .asset-upload__actions + */ &__actions { display: flex; + /** + * Styles for the progress section in the actions section. + * + * @class .asset-upload__progress + */ &__progress { margin-right: $space-34; + /** + * Styles for the file loaded count in the progress section. + * + * @class .file-loaded + */ .file-loaded { color: $color-font-base; font-size: $size-font-medium; } + /** + * Styles for the total size count in the progress section. + * + * @class .total-size + */ .total-size { color: $color-font-base; font-size: $size-font-medium; @@ -105,6 +196,11 @@ } } + /** + * Styles for the cancel upload button in the actions section. + * + * @class .cancel-upload + */ .cancel-upload { cursor: pointer; margin-top: calc(var(--space-2) + 0.063rem); diff --git a/ui/src/components/Common/FileUpload/upload.tsx b/ui/src/components/Common/FileUpload/upload.tsx index fcae15a4e..748f47799 100644 --- a/ui/src/components/Common/FileUpload/upload.tsx +++ b/ui/src/components/Common/FileUpload/upload.tsx @@ -19,6 +19,14 @@ import { returnFileSize, shortName } from '../../../utilities/functions'; import { UPLOAD_FILE_URL } from '../../../utilities/constants'; import './upload.scss'; +/** + * Renders a file component. + * + * @param {Object} props - The component props. + * @param {Object} props.file - The file object. + * @param {Function} props.handleRemove - The function to handle file removal. + * @returns {JSX.Element} The file component. + */ const File = ({ file, handleRemove }: any) => { return (
@@ -43,6 +51,12 @@ const File = ({ file, handleRemove }: any) => { ); }; +/** + * Renders an error file component. + * @param file - The file object. + * @param handleRemove - The function to handle file removal. + * @returns The JSX element representing the error file component. + */ const ErrorFile = ({ file, handleRemove }: any) => { return (
@@ -62,6 +76,11 @@ const ErrorFile = ({ file, handleRemove }: any) => { ); }; +/** + * Renders a component for interrupting the file upload process. + * @param props - The component props. + * @returns The rendered InterruptUpload component. + */ const InterruptUpload = (props: any) => { const handleProceed = () => { props.closeModal({ cancelUpload: true }); @@ -100,6 +119,9 @@ const InterruptUpload = (props: any) => { ); }; +/** + * Component for handling file uploads. + */ class Upload extends Component { state = { fileList: this.props.fileList, diff --git a/ui/src/components/Common/Modal/FilterModal/FilterModal.scss b/ui/src/components/Common/Modal/FilterModal/FilterModal.scss index a39ada6aa..65731c96f 100644 --- a/ui/src/components/Common/Modal/FilterModal/FilterModal.scss +++ b/ui/src/components/Common/Modal/FilterModal/FilterModal.scss @@ -1,5 +1,10 @@ @import '../../../../scss/variables'; +/** + * Styles for the FilterModal component. + * + * @class Filter__modal + */ .Filter__modal { width: $px-400; z-index: 40; @@ -9,29 +14,78 @@ border-radius: $px-10; } +/** + * Styles for the checkbox input and tick in the FilterModal component. + * + * The styles target the checkbox input element when it is checked, as well as the checkbox box element. + * The margin-top property sets the top margin of the checkbox tick. + * The font-size property sets the font size of the checkbox input. + */ .Filter__modal .Checkbox input:checked ~ .Checkbox__tick, .Filter__modal .Checkbox .Checkbox__box { margin-top: $px-4; font-size: $px-16; } +/** + * Styles for the FilterModal component. + * + * @class FilterModal + * @memberof components.Common.Modal + * + * @cssprop {string} cursor - Specifies the type of cursor to be displayed when hovering over the Filter__item. + * @cssprop {string} padding - Specifies the padding of the Filter__item. + */ .Filter__modal .Filter__item { cursor: pointer; padding: $px-10 $px-20; } +/** + * Styles for the hover effect on the FilterModal item. + * + * This selector targets the FilterModal item when it is being hovered over by the user. + * It sets the background color to a lightened version of the base white color. + */ .Filter__modal .Filter__item:hover { background-color: $color-base-white-10; } +/** + * Applies a specific color to the WordWrapper element when the Filter__item is being hovered over. + * + * @selector .Filter__modal .Filter__item:hover .WordWrapper + * @property color - The color to apply to the WordWrapper element. + * @value $color-brand-primary-base - The base color of the brand primary. + */ .Filter__modal .Filter__item:hover .WordWrapper { color: $color-brand-primary-base; } +/** + * Styles for the hover effect on the Checkbox__box element within the Filter__item element + * in the Filter__modal component. + * + * When the Filter__item element is hovered over, the Checkbox__box element will have a border + * with a thickness of 1.5 pixels and a color of $color-brand-primary-base. + */ .Filter__modal .Filter__item:hover .Checkbox__box { border: 1.5px solid $color-brand-primary-base; } +/** + * Styles for the header of the filter modal. + * + * @class .Filter__modal-header + * @memberof components.Common.Modal.FilterModal + * + * @property {string} background - The background color of the header. + * @property {string} border-bottom - The bottom border of the header. + * @property {string} font-size - The font size of the header. + * @property {string} height - The height of the header. + * @property {string} justify-content - The alignment of the content within the header. + * @property {string} padding - The padding of the header. + */ .Filter__modal-header { background: $color-base-white-10; border-bottom: 1px solid $color-brand-secondary-lightest; @@ -41,6 +95,20 @@ padding: $px-14 $px-16; } +/** + * Styles for the header container of the filter modal. + * + * @class .Filter__modal-header .Filter__modal-header-container + * @memberOf components.Common.Modal.FilterModal.FilterModal.scss + * + * @property {string} align-items - Specifies the alignment of the items inside the container. + * @property {string} display - Specifies the display behavior of the container. + * @property {string} color - Specifies the color of the text inside the container. + * @property {string} font-size - Specifies the font size of the text inside the container. + * @property {string} height - Specifies the height of the container. + * @property {string} line-height - Specifies the line height of the text inside the container. + * @property {string} font-weight - Specifies the font weight of the text inside the container. + */ .Filter__modal-header .Filter__modal-header-container { align-items: center; display: flex; @@ -53,10 +121,19 @@ font-weight: 600; } +/** + * Styles for the title element in the header of the filter modal. + */ .Filter__modal-header .Filter__modal-header-container .Filter__modal-header-title { font-weight: 600; } +/** + * Styles for the counter element in the filter modal header. + * + * This selector targets the counter element within the header container of the filter modal. + * It applies styles such as border, border radius, color, font size, font weight, etc. + */ .Filter__modal-header .Filter__modal-header-container .Filter__modal-header-counter { border: 1px solid $color-brand-secondary-lightest; border-radius: 1rem; @@ -74,30 +151,61 @@ width: fit-content; } +/** + * Styles for the search input inside the FilterModal component. + * + * @class .Filter__searchbox .Search--primary .Search__input + * @memberof components.Common.Modal.FilterModal + * + * @cssprop {number} height - The height of the search input in pixels. + */ .Filter__searchbox .Search--primary .Search__input { height: $px-35; } +/** + * Styles for the FilterModal component's modal content. + */ .Filter__modal-content { overflow: scroll; max-height: $px-250; overflow-x: hidden; } +/** + * Styles for the Filter__icon class. + * + * This class is used to style the filter icon in the modal. + * It sets the cursor to pointer, adds padding, and sets the border-radius to 10%. + */ .Filter__icon { cursor: pointer !important; padding: $px-5; border-radius: 10%; } +/** + * Styles for the hover effect on the Filter__icon element. + */ .Filter__icon:hover { background-color: rgba(108, 92, 231, 0.1); } +/** + * Sets the background color for the filter icon. + * + * @class .Filter__icon_bg_color + * @memberof components.Common.Modal.FilterModal + * + * @cssprop {Color} background-color - The background color of the filter icon. + */ .Filter__icon_bg_color { background-color: rgba(108, 92, 231, 0.1); } +/** + * Styles for the cancel button in the filter modal header. + */ .Filter__modal-header-cancel { border-radius: 25%; cursor: pointer; @@ -105,10 +213,24 @@ width: $px-24; } +/** + * Styles for the hover state of the cancel button in the filter modal header. + * + * @class .Filter__modal-header-cancel:hover + * @memberof components.Common.Modal.FilterModal.FilterModal.scss + * + * @cssprop {Color} background-color - The background color of the cancel button when hovered. + */ .Filter__modal-header-cancel:hover { background-color: $color-brand-secondary-lightest; } +/** + * Styles for the footer of the filter modal. + * + * @class .Filter__modal-footer + * @memberof components.Common.Modal.FilterModal + */ .Filter__modal-footer { display: flex; border-top: 1px solid $color-brand-secondary-lightest; @@ -117,6 +239,13 @@ padding: $px-20; } +/** + * Styles for the ".Filter__no-found" class. + * + * This class is used to style the text when no results are found in the filter modal. + * It sets the text alignment to center, vertical alignment to middle, line height to $px-50, + * font weight to 600, and font size to $px-14. + */ .Filter__no-found { text-align: center; vertical-align: middle; diff --git a/ui/src/components/Common/Modal/FilterModal/FilterModal.tsx b/ui/src/components/Common/Modal/FilterModal/FilterModal.tsx index 56b3e5d20..403f30cd6 100644 --- a/ui/src/components/Common/Modal/FilterModal/FilterModal.tsx +++ b/ui/src/components/Common/Modal/FilterModal/FilterModal.tsx @@ -5,6 +5,9 @@ import WordWrapper from '../../WordWrapper/WordWrapper'; import { IFilterStatusType, IFilterType } from './filterModal.interface'; import './FilterModal.scss'; +/** + * Props for the FilterModal component. + */ type Props = { title: string; list: IFilterType[]; diff --git a/ui/src/components/Common/Modal/FilterModal/filterModal.interface.ts b/ui/src/components/Common/Modal/FilterModal/filterModal.interface.ts index 311d0aab3..c36a49a87 100644 --- a/ui/src/components/Common/Modal/FilterModal/filterModal.interface.ts +++ b/ui/src/components/Common/Modal/FilterModal/filterModal.interface.ts @@ -4,6 +4,10 @@ export interface IFilterType { isChecked: boolean; } +/** + * Represents the interface for the filter status type. + * It is a key-value pair where the key is a string and the value is a boolean. + */ export interface IFilterStatusType { [key: string]: boolean; } diff --git a/ui/src/components/Common/NotificationModal/index.tsx b/ui/src/components/Common/NotificationModal/index.tsx index 8467cd796..382155925 100644 --- a/ui/src/components/Common/NotificationModal/index.tsx +++ b/ui/src/components/Common/NotificationModal/index.tsx @@ -14,26 +14,38 @@ interface Props { isopen: any; } -const NotificationModal = (props:Props) => { - return( - <> - {props?.closeModal(),props.isopen(false)}} className="text-capitalize" /> - -
- -
-
- +/** + * Renders a notification modal component. + * + * @param props - The component props. + * @returns The rendered notification modal component. + */ +const NotificationModal = (props: Props) => { + return ( + <> + { + props?.closeModal(); + props.isopen(false); + }} + className="text-capitalize" + /> + +
+ +
+
+ - - - - - - ) - + + + +
+ + ); } export default NotificationModal; \ No newline at end of file diff --git a/ui/src/components/Common/ProgressBar/progressBar.scss b/ui/src/components/Common/ProgressBar/progressBar.scss index fc0310e27..2e304456f 100644 --- a/ui/src/components/Common/ProgressBar/progressBar.scss +++ b/ui/src/components/Common/ProgressBar/progressBar.scss @@ -1,5 +1,14 @@ @import '../../../scss//variables'; +/** + * Styles for the ProgressBar component. + * + * The ProgressBar component displays a progress bar with a customizable bar and circle. + * + * @class ProgressBar + * @memberof components.Common + * + */ .ProgressBar { border-radius: $space-4; diff --git a/ui/src/components/Common/SaveChangesModal/index.tsx b/ui/src/components/Common/SaveChangesModal/index.tsx new file mode 100644 index 000000000..1086c4b6e --- /dev/null +++ b/ui/src/components/Common/SaveChangesModal/index.tsx @@ -0,0 +1,41 @@ + +import { + ModalBody, + ModalHeader, + ModalFooter, + ButtonGroup, + Button, + Paragraph +} from '@contentstack/venus-components'; + +interface Props { + closeModal: () => void; + isopen: any; + otherCmsTitle?: string; + saveContentType: () => void; + openContentType: () => void; +} + +const SaveChangesModal = (props:Props) => { + return( + <> + {props?.closeModal(),props.isopen(false)}} className="text-capitalize" /> + +
+ +
+
+ + + + + + + + ) + +} + +export default SaveChangesModal; \ No newline at end of file diff --git a/ui/src/components/Common/Settings/Settings.scss b/ui/src/components/Common/Settings/Settings.scss index 4c6377a39..5d7787582 100644 --- a/ui/src/components/Common/Settings/Settings.scss +++ b/ui/src/components/Common/Settings/Settings.scss @@ -34,13 +34,24 @@ } } +/** + * Styles for the Settings component. + */ + .Field { margin-bottom: $px-40; } + +/** + * Styles for hiding the left sidebar in the PageLayout component. + */ .PageLayout__leftSidebar--hide { display: none; } +/** + * Styles for the left sidebar. + */ .leftsidebar { margin-top: 0 !important; } diff --git a/ui/src/components/Common/Settings/index.tsx b/ui/src/components/Common/Settings/index.tsx index 7103fb796..4f9d0d982 100644 --- a/ui/src/components/Common/Settings/index.tsx +++ b/ui/src/components/Common/Settings/index.tsx @@ -34,6 +34,10 @@ import DeleteProjectModal from '../DeleteProjectModal'; import './Settings.scss'; import { RootState } from '../../../store'; +/** + * Renders the Settings component. + * This component is responsible for displaying and updating project settings. + */ const Settings = () => { const params: Params = useParams(); diff --git a/ui/src/components/Common/Settings/setting.interface.ts b/ui/src/components/Common/Settings/setting.interface.ts index f0abcc5e4..183b80b84 100644 --- a/ui/src/components/Common/Settings/setting.interface.ts +++ b/ui/src/components/Common/Settings/setting.interface.ts @@ -1,3 +1,6 @@ +/** + * Represents a project. + */ interface IProject { title: string; name: string; @@ -7,6 +10,9 @@ interface IProject { save_project: CTA; email: string; } +/** + * Represents a Call to Action (CTA) object. + */ interface CTA { open_in_new_tab: boolean; theme: string; @@ -18,6 +24,9 @@ interface IExecutionLogs { title: string; } +/** + * Represents a setting object. + */ export interface Setting { project?: IProject; execution_logs?: IExecutionLogs; diff --git a/ui/src/components/Common/WordWrapper/WordWrapper.tsx b/ui/src/components/Common/WordWrapper/WordWrapper.tsx index f9ca62381..9b86a8e31 100644 --- a/ui/src/components/Common/WordWrapper/WordWrapper.tsx +++ b/ui/src/components/Common/WordWrapper/WordWrapper.tsx @@ -10,6 +10,12 @@ type Props = { position?: string; }; +/** + * Renders a component that wraps text and provides a tooltip if the text exceeds a maximum length. + * + * @param {Props} props - The component props. + * @returns {JSX.Element} The rendered WordWrapper component. + */ function WordWrapper(props: Props) { const { maxLength, tooltipcontent, position = 'right' } = props; const text = props.text || ''; diff --git a/ui/src/components/Common/private-route.tsx b/ui/src/components/Common/private-route.tsx index 3ba2ce342..b7fa4da40 100644 --- a/ui/src/components/Common/private-route.tsx +++ b/ui/src/components/Common/private-route.tsx @@ -2,11 +2,22 @@ import { FC, ReactNode } from 'react'; import { Navigate, Outlet, useLocation } from 'react-router'; import { getDataFromLocalStorage } from '../../utilities/functions'; +/** + * Props for the PrivateRoute component. + */ type IProps = { children?: ReactNode; redirectTo: string; }; +/** + * Renders a private route component that checks if the user is authenticated. + * If the user is authenticated, it renders the child components. + * If the user is not authenticated, it redirects to the specified route. + * + * @param redirectTo - The route to redirect to if the user is not authenticated. + * @returns The private route component. + */ const PrivateRoute: FC = ({ redirectTo }: IProps) => { const isAuthenticated = !!getDataFromLocalStorage('app_token'); diff --git a/ui/src/components/Common/router.tsx b/ui/src/components/Common/router.tsx index 6df9932c2..0fffa4a2f 100644 --- a/ui/src/components/Common/router.tsx +++ b/ui/src/components/Common/router.tsx @@ -19,6 +19,10 @@ const MigrationLazyLoad = lazy(() => import('../../pages/Migration')); const ProjectsLazyLoad = lazy(() => import('../../pages/Projects')); const SettingsLazyLoad = lazy(() => import ('../Common/Settings')) +/** + * Renders the application router. + * @returns The application router component. + */ const AppRouter = () => { return ( diff --git a/ui/src/components/ContentMapper/index.scss b/ui/src/components/ContentMapper/index.scss index cf03e88cb..3d635b4bd 100644 --- a/ui/src/components/ContentMapper/index.scss +++ b/ui/src/components/ContentMapper/index.scss @@ -270,3 +270,7 @@ div .table-row { padding: $px-12; text-align: center; } +.table-icons:hover { + background-color: $color-brand-fail-light; + cursor: pointer; +} \ No newline at end of file diff --git a/ui/src/components/ContentMapper/index.tsx b/ui/src/components/ContentMapper/index.tsx index d7ae14301..291427249 100644 --- a/ui/src/components/ContentMapper/index.tsx +++ b/ui/src/components/ContentMapper/index.tsx @@ -48,7 +48,7 @@ import { optionsType, UidMap, ContentTypeMap, - Advanced + Advanced, } from './contentMapper.interface'; import { ItemStatusMapProp } from '@contentstack/venus-components/build/components/Table/types'; import { ModalObj } from '../Modal/modal.interface'; @@ -57,9 +57,11 @@ import { UpdatedSettings } from '../AdvancePropertise/advanceProperties.interfac // Components import SchemaModal from '../SchemaModal'; import AdvanceSettings from '../AdvancePropertise'; +import SaveChangesModal from '../Common/SaveChangesModal'; // Styles import './index.scss'; +import { MigrationResponse } from '../../services/api/service.interface'; const dummy_obj:any = { 'single_line_text':{ label : 'Single Line Textbox', @@ -164,7 +166,7 @@ const Fields: Mapping = { 'HTML Rich text Editor': 'JSON Rich Text Editor', 'JSON Rich Text Editor': 'JSON Rich Text Editor', // 'Multi line': - json: 'JSON Rich Text Editor', + json: ['HTML Rich text Editor', 'JSON Rich Text Editor'], URL: 'URL', file: 'File', number: 'Number', @@ -172,13 +174,18 @@ const Fields: Mapping = { boolean: 'Boolean', link: 'Link', reference: 'Reference', - dropdown: 'Select', + dropdown: 'dropdown', radio: 'Select', CheckBox: 'Select', global_field: 'Global' }; -const ContentMapper = () => { +type ContentMapperComponentProps = { + projectData: MigrationResponse; + +}; + +const ContentMapper = ({projectData}:ContentMapperComponentProps) => { /** ALL CONTEXT HERE */ const migrationData = useSelector((state:RootState)=>state?.migration?.migrationData); @@ -337,8 +344,8 @@ const ContentMapper = () => { // Get the stack status if it is empty or not const stackStatus = async () => { const contentTypeCount = await getStackStatus( - newMigrationData?.destination_stack?.selectedOrg?.value, - newMigrationData?.destination_stack?.selectedStack?.value + projectData?.org_id, + projectData?.destination_stack_id ); if (contentTypeCount?.data?.contenttype_count > 0) { @@ -360,7 +367,7 @@ const ContentMapper = () => { // Method to get fieldmapping const fetchFields = async (contentTypeId: string, searchText: string) => { - const { data } = await getFieldMapping(contentTypeId || '', 0, 30, searchText || ''); + const { data } = await getFieldMapping(contentTypeId || '', 0, 30, searchText || '', projectId); try { const itemStatusMap: ItemStatusMapProp = {}; @@ -407,8 +414,8 @@ const ContentMapper = () => { updateItemStatusMap({ ...itemStatusMapCopy }); setLoading(true); - const { data } = await getFieldMapping(contentTypeUid || '', skip, limit, searchText || ''); - + const { data } = await getFieldMapping(contentTypeUid || '', skip, limit, searchText || '', projectId); + const updateditemStatusMapCopy: ItemStatusMapProp = { ...itemStatusMap }; for (let index = startIndex; index <= stopIndex; index++) { @@ -425,10 +432,34 @@ const ContentMapper = () => { } }; + const [isModalOpen, setIsModalOpen] = useState(false); + // Method to change the content type + const handleOpenContentType = (i: number) => { + if (isDropDownChanged) { + setIsModalOpen(true); + return cbModal({ + component: (props: ModalObj) => ( + openContentType(i)} + /> + ), + modalProps: { + size: 'xsmall', + shouldCloseOnOverlayClick: false + } + }); + } else { + openContentType(i); + } + }; + const openContentType = (i: number) => { setActive(i); - const otherTitle = contentTypes?.[i]?.otherCmsTitle; setOtherCmsTitle(otherTitle); const option = contentTypeMapped?.[otherTitle] ?? 'Select Content Type'; @@ -438,7 +469,7 @@ const ContentMapper = () => { fetchFields(contentTypes?.[i]?.id ?? '', searchText || ''); setotherCmsUid(contentTypes?.[i]?.otherCmsUid); setSelectedContentType(contentTypes?.[i]); - }; + } // Function to get exisiting content types list const fetchExistingContentTypes = async () => { @@ -484,7 +515,7 @@ const ContentMapper = () => {
{data?.otherCmsField}
- Other CMS Type: {data?.otherCmsType} + Type: {data?.otherCmsType}
UID: {data?.uid}
@@ -560,15 +591,15 @@ const ContentMapper = () => { } }); }; + const SelectAccessor = (data: FieldMapType) => { - //const OptionsForRow = Fields[data?.backupFieldType as keyof Mapping]; const OptionsForRow = dummy_obj?.[data?.backupFieldType]?.options ; const initialOption = { label: dummy_obj?.[data?.ContentstackFieldType]?.label, value: dummy_obj?.[data?.ContentstackFieldType]?.label, }; - let option:any; + let option: FieldTypes[]; if (Array.isArray(OptionsForRow)) { option = OptionsForRow.map((option) => ({ label: option, @@ -579,12 +610,18 @@ const ContentMapper = () => { label, value, })); - }else{ + + if (option?.length === 1 && option?.[0]?.label === initialOption?.label) { + option = [{ label: "No option available", value: "No option available" }]; + } + + } else { option = [{ label: OptionsForRow, value: OptionsForRow }] } - const fieldLabel = data?.ContentstackFieldType === 'url' || data?.ContentstackFieldType === 'group' - ? data?.ContentstackFieldType : option?.[0]?.label + const fieldLabel = data?.ContentstackFieldType === 'url' || data?.ContentstackFieldType === 'group' + ? data?.ContentstackFieldType : option?.[0]?.label + return (
@@ -885,19 +922,23 @@ const ContentMapper = () => { notificationContent: { text: 'Content type saved successfully' }, notificationProps: { position: 'bottom-center', - hideProgressBar: false + hideProgressBar: true }, type: 'success' }); setisDropDownCHanged(false); setisContentTypeMapped(true); setisContentTypeSaved(true); + + setFilteredContentTypes(filteredContentTypes?.map(ct => + ct?.id === data?.updatedContentType?.id ? { ...ct, status: data?.updatedContentType?.status } : ct + )) } else { Notification({ notificationContent: { text: data?.error?.message }, notificationProps: { position: 'bottom-center', - hideProgressBar: false + hideProgressBar: true }, type: 'error' }); @@ -959,6 +1000,13 @@ const ContentMapper = () => { }); } else { const { data } = await fetchExistingContentType(projectId, OtherContentType?.id || ''); + + const index = contentTypesList.findIndex(ct => ct?.uid === data?.uid); + if(index != -1){ + contentTypesList[index] = data; + } + + setContentTypesList(contentTypesList) setContentTypeSchema(data?.schema) } } @@ -1103,22 +1151,19 @@ const ContentMapper = () => {
  • openContentType(index)} - onKeyDown={() => openContentType(index)} + onClick={() => handleOpenContentType(index)} + onKeyDown={() => handleOpenContentType(index)} >
    - {content?.type === "Content Type" + {content?.type === "content_type" ? - : content?.type === "Global Field" - ? - : <> + : } {content?.otherCmsTitle && {content?.otherCmsTitle} }
    -
    {icon && ( @@ -1169,7 +1214,7 @@ const ContentMapper = () => {
    {!IsEmptyStack && ( - + )} @@ -1179,6 +1224,7 @@ const ContentMapper = () => { size="small" version="v2" onClick={handleResetContentType} + hover={true} /> diff --git a/ui/src/pages/Migration/index.tsx b/ui/src/pages/Migration/index.tsx index f9571a0d2..ab0a88f6f 100644 --- a/ui/src/pages/Migration/index.tsx +++ b/ui/src/pages/Migration/index.tsx @@ -124,7 +124,8 @@ const Migration = () => { title:'Destination Stack' }, { - data: , + data: , id:'3', title:'Content Mapping' }, diff --git a/ui/src/services/api/migration.service.ts b/ui/src/services/api/migration.service.ts index e680370a2..833135f93 100644 --- a/ui/src/services/api/migration.service.ts +++ b/ui/src/services/api/migration.service.ts @@ -105,12 +105,13 @@ export const getFieldMapping = async ( contentTypeId: string, skip: number, limit: number, - searchText: string + searchText: string, + projectId:string ) => { try { const encodedSearchText = encodeURIComponent(searchText) ; return await getCall( - `${API_VERSION}/mapper/fieldMapping/${contentTypeId}/${skip}/${limit}/${encodedSearchText}?`, + `${API_VERSION}/mapper/fieldMapping/${projectId}/${contentTypeId}/${skip}/${limit}/${encodedSearchText}?`, options ); } catch (error: any) { diff --git a/uplaode-api/migration-sitecore/libs/contenttypes.js b/uplaode-api/migration-sitecore/libs/contenttypes.js index 570384ecc..dbfd138b4 100644 --- a/uplaode-api/migration-sitecore/libs/contenttypes.js +++ b/uplaode-api/migration-sitecore/libs/contenttypes.js @@ -464,7 +464,7 @@ const contentTypeMapper = ({ components, standardValues, content_type, basePath, if (isUrlfound === undefined) { mainSchema?.unshift( { - uid: "Url", + uid: "url", "otherCmsField": "url", "otherCmsType": "text", "contentstackField": "Url", @@ -478,7 +478,7 @@ const contentTypeMapper = ({ components, standardValues, content_type, basePath, ) if (isPresent === undefined) { mainSchema.unshift({ - uid: "Title", + uid: "title", "otherCmsField": "title", "otherCmsType": "text", "contentstackField": "Title", diff --git a/uplaode-api/src/config/index.ts b/uplaode-api/src/config/index.ts index d0722ca5a..95ea0bf7d 100644 --- a/uplaode-api/src/config/index.ts +++ b/uplaode-api/src/config/index.ts @@ -9,5 +9,5 @@ export default { bucketName: 'migartion-test', buketKey: 'project/package 45.zip' }, - localPath: '/Users/snehal.pimple/Desktop/package 45.zip' + localPath: '/Users/rohit/Desktop/package 45.zip' }; diff --git a/uplaode-api/src/controllers/sitecore/index.ts b/uplaode-api/src/controllers/sitecore/index.ts index 2ca0823d0..b5f1e0625 100644 --- a/uplaode-api/src/controllers/sitecore/index.ts +++ b/uplaode-api/src/controllers/sitecore/index.ts @@ -17,10 +17,23 @@ const createSitecoreMapper = async (filePath: string = "", projectId: string | s if (infoMap?.contentTypeUids?.length) { const fieldMapping: any = { contentTypes: [] }; for await (const contentType of infoMap?.contentTypeUids ?? []) { - fieldMapping?.contentTypes?.push( - JSON.parse(readFileSync(`${infoMap?.path}/content_types/${contentType}`, 'utf8')) - ); + const fileContent = readFileSync(`${infoMap?.path}/content_types/${contentType}`, 'utf8'); + const jsonfileContent = JSON.parse(fileContent); + jsonfileContent.type = "content_type"; + fieldMapping?.contentTypes?.push(jsonfileContent); } + + + const fileContent = readFileSync(`${infoMap?.path}/global_fields/globalfields.json`, 'utf8'); + const jsonfileContent = JSON.parse(fileContent); + for (const key in jsonfileContent) { + if (jsonfileContent.hasOwnProperty(key)) { + const element = jsonfileContent[key]; + element.type = "global_field"; + fieldMapping.contentTypes.push(element); + } + } + const config = { method: 'post', maxBodyLength: Infinity,