diff --git a/.github/instructions/nx.instructions.md b/.github/instructions/nx.instructions.md new file mode 100644 index 0000000000..998edcbe15 --- /dev/null +++ b/.github/instructions/nx.instructions.md @@ -0,0 +1,32 @@ +--- +applyTo: '**' +--- + +// This file is automatically generated by Nx Console + +You are in an nx workspace using Nx 20.0.8 and pnpm as the package manager. + +You have access to the Nx MCP server and the tools it provides. Use them. Follow these guidelines in order to best help the user: + +# General Guidelines +- When answering questions, use the nx_workspace tool first to gain an understanding of the workspace architecture +- For questions around nx configuration, best practices or if you're unsure, use the nx_docs tool to get relevant, up-to-date docs!! Always use this instead of assuming things about nx configuration +- If the user needs help with an Nx configuration or project graph error, use the 'nx_workspace' tool to get any errors +- To help answer questions about the workspace structure or simply help with demonstrating how tasks depend on each other, use the 'nx_visualize_graph' tool + +# Generation Guidelines +If the user wants to generate something, use the following flow: + +- learn about the nx workspace and any specifics the user needs by using the 'nx_workspace' tool and the 'nx_project_details' tool if applicable +- get the available generators using the 'nx_generators' tool +- decide which generator to use. If no generators seem relevant, check the 'nx_available_plugins' tool to see if the user could install a plugin to help them +- get generator details using the 'nx_generator_schema' tool +- you may use the 'nx_docs' tool to learn more about a specific generator or technology if you're unsure +- decide which options to provide in order to best complete the user's request. Don't make any assumptions and keep the options minimalistic +- open the generator UI using the 'nx_open_generate_ui' tool +- wait for the user to finish the generator +- read the generator log file using the 'nx_read_generator_log' tool +- use the information provided in the log file to answer the user's question or continue with what they were doing + + + diff --git a/backend/core-api/src/modules/contacts/trpc/company.ts b/backend/core-api/src/modules/contacts/trpc/company.ts index 2e67956d96..abf5fdd5ae 100644 --- a/backend/core-api/src/modules/contacts/trpc/company.ts +++ b/backend/core-api/src/modules/contacts/trpc/company.ts @@ -19,7 +19,6 @@ export const companyTrpcRouter = t.router({ findOne: t.procedure.input(z.any()).query(async ({ ctx, input }) => { const { query } = input; const { models } = ctx; - const defaultFilter = { status: { $ne: 'deleted' } }; if (query.companyPrimaryName) { diff --git a/backend/plugins/cars_api/.gitignore b/backend/plugins/cars_api/.gitignore new file mode 100644 index 0000000000..3b24e4a138 --- /dev/null +++ b/backend/plugins/cars_api/.gitignore @@ -0,0 +1,4 @@ +node_modules +dist +.env +*.log diff --git a/backend/plugins/cars_api/package.json b/backend/plugins/cars_api/package.json new file mode 100644 index 0000000000..679cc27eee --- /dev/null +++ b/backend/plugins/cars_api/package.json @@ -0,0 +1,18 @@ +{ + "name": "cars_api", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "dev": "tsx watch src/main.ts", + "build": "tsc --project tsconfig.build.json && tsc-alias -p tsconfig.build.json", + "start": "node -r tsconfig-paths/register dist/src/main.js" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "erxes-api-shared": "workspace:^" + }, + "devDependencies": {} +} \ No newline at end of file diff --git a/backend/plugins/cars_api/project.json b/backend/plugins/cars_api/project.json new file mode 100644 index 0000000000..b21bbc8d2e --- /dev/null +++ b/backend/plugins/cars_api/project.json @@ -0,0 +1,57 @@ +{ + "name": "cars_api", + "$schema": "../../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "backend/plugins/cars_api/src", + "projectType": "application", + "tags": [], + "targets": { + "build": { + "executor": "nx:run-commands", + "cache": true, + "options": { + "cwd": "backend/plugins/cars_api", + "commands": [ + "pnpm build" + ] + }, + "dependsOn": [ + "^build", + "build:packageJson" + ] + }, + "build:packageJson": { + "executor": "@nx/js:tsc", + "options": { + "main": "backend/plugins/cars_api/dist/src/main.js", + "tsConfig": "backend/plugins/cars_api/tsconfig.build.json", + "outputPath": "backend/plugins/cars_api/dist", + "updateBuildableProjectDepsInPackageJson": true, + "buildableProjectDepsInPackageJsonType": "dependencies" + } + }, + "start": { + "executor": "nx:run-commands", + "dependsOn": [ + "typecheck", + "build" + ], + "options": { + "cwd": "backend/plugins/cars_api", + "command": "NODE_ENV=development node dist/src/main.js" + } + }, + "serve": { + "executor": "nx:run-commands", + "options": { + "cwd": "backend/plugins/cars_api", + "command": "NODE_ENV=development pnpm dev" + } + }, + "docker-build": { + "dependsOn": [ + "build" + ], + "command": "docker build -f backend/plugins/cars_api/Dockerfile . -t erxes/erxes-next-cars_api" + } + } +} \ No newline at end of file diff --git a/backend/plugins/cars_api/src/apollo/resolvers/index.ts b/backend/plugins/cars_api/src/apollo/resolvers/index.ts new file mode 100644 index 0000000000..d4b64cf3b6 --- /dev/null +++ b/backend/plugins/cars_api/src/apollo/resolvers/index.ts @@ -0,0 +1,16 @@ +import { apolloCustomScalars } from 'erxes-api-shared/utils'; +import { customResolvers } from './resolvers'; +import { mutations } from './mutations'; +import { queries } from './queries'; +const resolvers: any = { + Mutation: { + ...mutations, + }, + Query: { + ...queries, + }, + ...apolloCustomScalars, + ...customResolvers, +}; + +export default resolvers; diff --git a/backend/plugins/cars_api/src/apollo/resolvers/mutations.ts b/backend/plugins/cars_api/src/apollo/resolvers/mutations.ts new file mode 100644 index 0000000000..f0470d1252 --- /dev/null +++ b/backend/plugins/cars_api/src/apollo/resolvers/mutations.ts @@ -0,0 +1,7 @@ +import { carMutations } from '~/modules/cars/graphql/resolvers/mutations/carMutations'; +import { carCategoryMutations } from '~/modules/cars/graphql/resolvers/mutations/categoryMutations'; + +export const mutations = { + ...carMutations, + ...carCategoryMutations, +}; diff --git a/backend/plugins/cars_api/src/apollo/resolvers/queries.ts b/backend/plugins/cars_api/src/apollo/resolvers/queries.ts new file mode 100644 index 0000000000..fba4c93de3 --- /dev/null +++ b/backend/plugins/cars_api/src/apollo/resolvers/queries.ts @@ -0,0 +1,7 @@ +import { carQueries } from '~/modules/cars/graphql/resolvers/queries/carQueries'; +import { CarCategoryQueries } from '~/modules/cars/graphql/resolvers/queries/categoryQueries'; + +export const queries = { + ...carQueries, + ...CarCategoryQueries, +}; diff --git a/backend/plugins/cars_api/src/apollo/resolvers/resolvers.ts b/backend/plugins/cars_api/src/apollo/resolvers/resolvers.ts new file mode 100644 index 0000000000..d39466c5ef --- /dev/null +++ b/backend/plugins/cars_api/src/apollo/resolvers/resolvers.ts @@ -0,0 +1,7 @@ +import carResolver from '~/modules/cars/graphql/resolvers/customResolvers/carResolver'; +import categoryResolver from '~/modules/cars/graphql/resolvers/customResolvers/categoryResolver'; + +export const customResolvers = { + Car: carResolver, + categoryResolver, +}; diff --git a/backend/plugins/cars_api/src/apollo/schema/schema.ts b/backend/plugins/cars_api/src/apollo/schema/schema.ts new file mode 100644 index 0000000000..52b21ad3e5 --- /dev/null +++ b/backend/plugins/cars_api/src/apollo/schema/schema.ts @@ -0,0 +1,31 @@ +import { + mutations as carMutations, + queries as carQueries, + types as carTypes, +} from '~/modules/cars/graphql/schemas/cars'; + +import { + mutations as carCategoryMutations, + queries as CarCategoryQueries, + types as carCategoryTypes, +} from '~/modules/cars/graphql/schemas/category'; + +import { TypeExtensions } from '~/modules/cars/graphql/schemas/extension'; + +export const types = ` + ${TypeExtensions} + ${carTypes}, + ${carCategoryTypes} +`; + +export const queries = ` + ${carQueries}, + ${CarCategoryQueries} +`; + +export const mutations = ` + ${carMutations}, + ${carCategoryMutations} +`; + +export default { types, queries, mutations }; diff --git a/backend/plugins/cars_api/src/apollo/typeDefs.ts b/backend/plugins/cars_api/src/apollo/typeDefs.ts new file mode 100644 index 0000000000..0b1ea5e35c --- /dev/null +++ b/backend/plugins/cars_api/src/apollo/typeDefs.ts @@ -0,0 +1,17 @@ +import { apolloCommonTypes } from 'erxes-api-shared/utils'; +import { DocumentNode } from 'graphql'; +import { gql } from 'graphql-tag'; +import { mutations, queries, types } from '~/apollo/schema/schema'; + +export const typeDefs = async (): Promise => { + return gql` + ${apolloCommonTypes} + ${types} + extend type Query { + ${queries} + } + extend type Mutation { + ${mutations} + } + `; +}; diff --git a/backend/plugins/cars_api/src/connectionResolvers.ts b/backend/plugins/cars_api/src/connectionResolvers.ts new file mode 100644 index 0000000000..b5cc72f956 --- /dev/null +++ b/backend/plugins/cars_api/src/connectionResolvers.ts @@ -0,0 +1,37 @@ +import { createGenerateModels } from 'erxes-api-shared/utils'; +import { IMainContext } from 'erxes-api-shared/core-types'; +import { ICarDocument } from '~/modules/cars/@types/car'; + +import mongoose from 'mongoose'; + +import { loadCarClass, ICarModel } from '~/modules/cars/db/models/carModel'; +import { + ICarCategoryModel, + loadCarCategoryClass, +} from './modules/cars/db/models/categoryModel'; +import { ICarCategoryDocument } from './modules/cars/@types/category'; + +export interface IModels { + Cars: ICarModel; + CarCategories: ICarCategoryModel; +} + +export interface IContext extends IMainContext { + models: IModels; + commonQuerySelector: string; +} + +export const loadClasses = (db: mongoose.Connection): IModels => { + const models = {} as IModels; + + models.Cars = db.model('cars', loadCarClass(models)); + + models.CarCategories = db.model( + 'car_categories', + loadCarCategoryClass(models), + ); + + return models; +}; + +export const generateModels = createGenerateModels(loadClasses); diff --git a/backend/plugins/cars_api/src/main.ts b/backend/plugins/cars_api/src/main.ts new file mode 100644 index 0000000000..89f2ca835a --- /dev/null +++ b/backend/plugins/cars_api/src/main.ts @@ -0,0 +1,31 @@ +import { startPlugin } from 'erxes-api-shared/utils'; +import { typeDefs } from '~/apollo/typeDefs'; +import { carsTrpcRouter } from '~/trpc/init-trpc'; +import resolvers from './apollo/resolvers'; +import { generateModels } from './connectionResolvers'; + +startPlugin({ + name: 'cars', + port: 33010, + graphql: async () => ({ + typeDefs: await typeDefs(), + resolvers, + }), + apolloServerContext: async (subdomain, context) => { + const models = await generateModels(subdomain); + + context.models = models; + + return context; + }, + trpcAppRouter: { + router: carsTrpcRouter, + createContext: async (subdomain, context) => { + const models = await generateModels(subdomain); + + context.models = models; + + return context; + }, + }, +}); diff --git a/backend/plugins/cars_api/src/modules/cars/@types/car.ts b/backend/plugins/cars_api/src/modules/cars/@types/car.ts new file mode 100644 index 0000000000..745f2fdbd0 --- /dev/null +++ b/backend/plugins/cars_api/src/modules/cars/@types/car.ts @@ -0,0 +1,40 @@ +import { Document } from 'mongoose'; +import { + IAttachment, + ICursorPaginateParams, + IListParams, +} from 'erxes-api-shared/core-types'; + +export interface ICar { + ownerId: string; + plateNumber: string; + vinNumber: string; + colorCode: string; + categoryId: string; + bodyType: string; + fuelType: string; + gearBox: string; + vintageYear: number; + importYear: number; + status: string; + description: string; + tagIds: string[]; + mergeIds: string[]; + searchText: string; + attachment: IAttachment; +} + +export interface ICarDocument extends ICar, Document { + _id: string; + createdAt: Date; + updatedAt: Date; + ownerId: string; + searchText: string; +} + +export interface ICarParams extends IListParams, ICursorPaginateParams { + ids?: string[]; + categoryId?: string; + searchValue?: string; + tag?: string; +} diff --git a/backend/plugins/cars_api/src/modules/cars/@types/category.ts b/backend/plugins/cars_api/src/modules/cars/@types/category.ts new file mode 100644 index 0000000000..3b6e53d5c0 --- /dev/null +++ b/backend/plugins/cars_api/src/modules/cars/@types/category.ts @@ -0,0 +1,19 @@ +import { Document } from 'mongoose'; +import { IAttachment } from 'erxes-api-shared/core-types'; + +export interface ICarCategory { + name: string; + code: string; + order: string; + parentId: string; + description: string; + image: IAttachment; + secondaryImages: IAttachment[]; + productCategoryId: string; +} + +export interface ICarCategoryDocument extends ICarCategory, Document { + _id: string; + createdAt: Date; + updatedAt: Date; +} diff --git a/backend/plugins/cars_api/src/modules/cars/constants.ts b/backend/plugins/cars_api/src/modules/cars/constants.ts new file mode 100644 index 0000000000..748dcec5d3 --- /dev/null +++ b/backend/plugins/cars_api/src/modules/cars/constants.ts @@ -0,0 +1,47 @@ +export const GEARBOX = { + UNKNOWN: '', + AUTOMATIC: 'automatic', + MANUAL: 'manual', + CVT: 'cvt', + SEMI_AUTOMATIC: 'semi_automatic', + ALL: ['', 'automatic', 'manual', 'cvt', 'semi_automatic'], +}; + +export const FUEL_TYPES = { + UNKNOWN: '', + HYBRID: 'hybrid', + PETROL: 'petrol', + DIESEL: 'diesel', + FLEXIFUEL: 'flexiFuel', + ELECTRIC: 'electric', + ALL: ['', 'hybrid', 'petrol', 'diesel', 'flexiFuel', 'electric'], +}; + +export const BODY_TYPES = { + UNKNOWN: '', + SEDAN: 'sedan', + SUV: 'suv', + COMPACT: 'compact', + WAGON: 'wagon', + VAN: 'van', + HATCHBACK: 'hatchback', + PICKUP: 'pickUp', + SPORT_COUPE: 'sport_coupe', + ALL: [ + '', + 'sedan', + 'suv', + 'compact', + 'wagon', + 'van', + 'hatchback', + 'pickUp', + 'sport_coupe', + ], +}; + +export const STATUSES = { + ACTIVE: 'active', + DELETED: 'deleted', + ALL: ['active', 'deleted'], +}; diff --git a/backend/plugins/cars_api/src/modules/cars/db/definitions/car.ts b/backend/plugins/cars_api/src/modules/cars/db/definitions/car.ts new file mode 100644 index 0000000000..4e2cc8933a --- /dev/null +++ b/backend/plugins/cars_api/src/modules/cars/db/definitions/car.ts @@ -0,0 +1,51 @@ +import { Schema } from 'mongoose'; + +import { schemaWrapper } from 'erxes-api-shared/utils'; +import { BODY_TYPES, FUEL_TYPES, GEARBOX, STATUSES } from '../../constants'; +import { + customFieldSchema, + attachmentSchema, +} from 'erxes-api-shared/core-modules'; + +export const carSchema = schemaWrapper( + new Schema( + { + ownerId: { type: String, label: 'Owner' }, + plateNumber: { type: String, label: 'Plate number', index: true }, + vinNumber: { type: String, label: 'VIN number', index: true }, + colorCode: { type: String, label: 'colorCode' }, + categoryId: { + type: String, + label: 'Category', + required: true, + index: true, + }, + bodyType: { type: String, label: 'Body Type', enum: BODY_TYPES.ALL }, + fuelType: { type: String, label: 'Brand', enum: FUEL_TYPES.ALL }, + gearBox: { type: String, label: 'Gear box', enum: GEARBOX.ALL }, + vintageYear: { type: Number, label: 'Vintage year', required: true }, + importYear: { type: Number, label: 'Imported year', required: true }, + status: { + type: String, + label: 'Status', + enum: STATUSES.ALL, + index: true, + }, + description: { type: String, label: 'Description' }, + tagIds: { type: [String], label: 'Tags' }, + mergeIds: { type: [String], label: 'Merged companies' }, + searchText: { type: String, index: true }, + attachment: { + type: attachmentSchema, + label: 'attachment', + }, + customFieldsDays: { + type: customFieldSchema, + label: 'Custom fields data ', + }, + }, + { + timestamps: true, + }, + ), +); diff --git a/backend/plugins/cars_api/src/modules/cars/db/definitions/category.ts b/backend/plugins/cars_api/src/modules/cars/db/definitions/category.ts new file mode 100644 index 0000000000..87f10d88e2 --- /dev/null +++ b/backend/plugins/cars_api/src/modules/cars/db/definitions/category.ts @@ -0,0 +1,25 @@ +import { Schema } from 'mongoose'; + +import { schemaWrapper } from 'erxes-api-shared/utils'; +import { attachmentSchema } from 'erxes-api-shared/core-modules'; + +export const categorySchema = schemaWrapper( + new Schema( + { + name: { type: String, label: 'Name', required: true }, + code: { type: String, label: 'Code', required: true, unique: true }, + order: { type: String, label: 'Order', required: true }, + parentId: { type: String, label: 'Parent' }, + description: { type: String, label: 'Description' }, + image: { type: attachmentSchema, label: 'Image' }, + secondaryImage: { + type: [attachmentSchema], + label: 'Secondary images', + }, + productCategoryId: { type: String, label: 'Product' }, + }, + { + timestamps: true, + }, + ), +); diff --git a/backend/plugins/cars_api/src/modules/cars/db/models/carModel.ts b/backend/plugins/cars_api/src/modules/cars/db/models/carModel.ts new file mode 100644 index 0000000000..1553db720d --- /dev/null +++ b/backend/plugins/cars_api/src/modules/cars/db/models/carModel.ts @@ -0,0 +1,151 @@ +import { Model } from 'mongoose'; +import { IModels } from '~/connectionResolvers'; +import { carSchema } from '~/modules/cars/db/definitions/car'; +import { ICar, ICarDocument } from '~/modules/cars/@types/car'; +import { validSearchText } from 'erxes-api-shared/utils'; +import { any } from 'zod'; + +export interface ICarModel extends Model { + fillSearchText(doc: ICar); + carDetail(_id: string): Promise; + cars(): Promise; + carsAdd(doc: ICar): Promise; + carsEdit(_id: string, doc: ICar): Promise; + carsRemove(ModuleId: string[]): Promise<{ ok: number }>; + carDetail(_id: string): Promise; + mergeCars(carIds: string[], carFields: ICar): Promise; +} + +export const loadCarClass = (models: IModels) => { + class Cars { + public static fillSearchText(doc: ICar) { + return validSearchText([ + doc.plateNumber || '', + doc.vinNumber || '', + doc.description || '', + doc.categoryId || '', + ]); + } + + public static async checkDuplication( + carFields: { + plateNumber?: string; + vinNumber?: string; + }, + idsToExclude?: string[] | string, + ) { + const query: { status: {}; [key: string]: any } = { + status: { $ne: 'Deleted' }, + }; + let previousEntry; + + // Adding exclude operator to the query + if (idsToExclude) { + query._id = { $nin: idsToExclude }; + } + + if (carFields.plateNumber) { + // check duplication from primaryName + previousEntry = await models.Cars.find({ + ...query, + plateNumber: carFields.plateNumber, + }); + + if (previousEntry.length > 0) { + throw new Error('Duplicated plate number'); + } + } + + if (carFields.vinNumber) { + // check duplication from code + previousEntry = await models.Cars.find({ + ...query, + vinNumber: carFields.vinNumber, + }); + + if (previousEntry.length > 0) { + throw new Error('Duplicated VIN number'); + } + } + } + + /** + * Retrieves cars + */ + public static async carDetail(_id: string) { + const cars = await models.Cars.findOne({ _id }).lean(); + + if (!cars) { + throw new Error('Car not found'); + } + + return cars; + } + + /** + * Retrieves all carss + */ + public static async cars(): Promise { + return models.Cars.find().lean(); + } + + /** + * Create a cars + */ + public static async carsAdd(doc: ICar): Promise { + return models.Cars.create({ + ...doc, + createdAt: new Date(), + modifiedAt: new Date(), + searchText: models.Cars.fillSearchText(doc), + }); + } + + /* + * Update cars + */ + public static async carsEdit(_id: string, doc: ICar) { + const car = await models.Cars.carDetail(_id); + + const searchText: string = models.Cars.fillSearchText( + Object.assign(car, doc), + ); + + await models.Cars.updateOne( + { _id }, + { $set: { ...doc, searchText, modifiedAt: new Date() } }, + ); + + return models.Cars.findOne({ _id }); + } + + /** + * Remove cars + */ + public static async carsRemove(carIds: string[]) { + return models.Cars.deleteMany({ _id: { $in: carIds } }); + } + + // Merge cars + + public static async mergeCars(carIds: string[], carFields: ICar) { + await this.checkDuplication(carFields, carIds); + + for (const carId of carIds) { + models.Cars.carDetail(carId); + await models.Cars.findByIdAndUpdate(carId, { + $set: { status: 'Deleted' }, + }); + } + + return await models.Cars.carsAdd({ + ...carFields, + mergeIds: carIds, + }); + } + } + + carSchema.loadClass(Cars); + + return carSchema; +}; diff --git a/backend/plugins/cars_api/src/modules/cars/db/models/categoryModel.ts b/backend/plugins/cars_api/src/modules/cars/db/models/categoryModel.ts new file mode 100644 index 0000000000..632d34113a --- /dev/null +++ b/backend/plugins/cars_api/src/modules/cars/db/models/categoryModel.ts @@ -0,0 +1,110 @@ +import { Model } from 'mongoose'; +import { ICarCategory, ICarCategoryDocument } from '../../@types/category'; +import { IModels } from '~/connectionResolvers'; +import { categorySchema } from '../definitions/category'; + +export interface ICarCategoryModel extends Model { + carCategoryDetail(selector: any): Promise; + carsCategoryAdd(doc: ICarCategory): Promise; + carsCategories(doc: ICarCategory): Promise; + carsCategoriesEdit( + _id: string, + doc: ICarCategory, + ): Promise; + carsCategoriesRemove(ModuleId: string): Promise; + generateOrder( + parentCategory: any, + doc: ICarCategory, + ): Promise; +} + +export const loadCarCategoryClass = (models: IModels) => { + class CarCategories { + public static async carCategoryDetail(_id: string) { + return await models.CarCategories.findOne({ _id }).lean(); + } + + public static async carsCategories(): Promise { + return models.CarCategories.find().lean(); + } + + public static async carsCategoryAdd( + doc: ICarCategory, + ): Promise { + const parentCategory = doc.parentId + ? await models.CarCategories.findOne({ _id: doc.parentId }).lean() + : undefined; + + // Generatingg order + doc.order = await this.generateOrder(parentCategory, doc); + + return models.CarCategories.create(doc); + } + + public static async carsCategoriesEdit(_id: string, doc: ICarCategory) { + const parentCategory = doc.parentId + ? await models.CarCategories.findOne({ _id: doc.parentId }).lean() + : undefined; + + if (parentCategory && parentCategory.parentId === _id) { + throw new Error('Cannot change category'); + } + + // Generatingg order + doc.order = await this.generateOrder(parentCategory, doc); + + const carCategory = await models.CarCategories.carCategoryDetail(_id); + + const childCategories = await models.CarCategories.find({ + $and: [ + { order: { $regex: new RegExp(carCategory.order, 'i') } }, + { _id: { $ne: _id } }, + ], + }); + + childCategories.forEach(async (category) => { + let order = category.order; + + order = order.replace(carCategory.order, doc.order); + + await models.CarCategories.updateOne( + { _id: category._id }, + { $set: { order } }, + ); + }); + + await models.CarCategories.updateOne({ _id }, { $set: doc }); + + return models.CarCategories.findOne({ _id }); + } + + public static async carsCategoriesRemove(_id: string) { + await models.CarCategories.carCategoryDetail(_id); + + let count = await models.Cars.countDocuments({ + categoryId: _id, + }); + + count += await models.CarCategories.countDocuments({ + parentId: _id, + }); + + if (count > 0) { + throw new Error("Can't remove a car category"); + } + + return await models.CarCategories.deleteOne({ _id }); + } + + public static async generateOrder(parentCategory: any, doc: ICarCategory) { + const order = parentCategory + ? `${parentCategory.order}/${doc.code}` + : `${doc.code}`; + + return order; + } + } + + categorySchema.loadClass(CarCategories); + return categorySchema; +}; diff --git a/backend/plugins/cars_api/src/modules/cars/graphql/resolvers/customResolvers/carResolver.ts b/backend/plugins/cars_api/src/modules/cars/graphql/resolvers/customResolvers/carResolver.ts new file mode 100644 index 0000000000..8e53d9229e --- /dev/null +++ b/backend/plugins/cars_api/src/modules/cars/graphql/resolvers/customResolvers/carResolver.ts @@ -0,0 +1,24 @@ +import { IContext } from '~/connectionResolvers'; +import { ICarDocument } from '~/modules/cars/@types/car'; + +export default { + category: async (car: ICarDocument, _args: any, { models }: IContext) => { + return models.CarCategories.findOne({ _id: car.categoryId }); + }, + + customer: async (car: ICarDocument) => { + if (!car?.ownerId) { + return null; + } + + return { __typename: 'Customer', _id: car.ownerId }; + }, + + tags(car: ICarDocument) { + if (!car.tagIds?.length) { + return []; + } + + return car.tagIds.map((_id) => ({ __typename: 'Tag', _id })); + }, +}; diff --git a/backend/plugins/cars_api/src/modules/cars/graphql/resolvers/customResolvers/categoryResolver.ts b/backend/plugins/cars_api/src/modules/cars/graphql/resolvers/customResolvers/categoryResolver.ts new file mode 100644 index 0000000000..016a7e0ab2 --- /dev/null +++ b/backend/plugins/cars_api/src/modules/cars/graphql/resolvers/customResolvers/categoryResolver.ts @@ -0,0 +1,24 @@ +import { ICarCategoryDocument } from '~/modules/cars/@types/category'; +import { IContext } from '~/connectionResolvers'; + +export default { + __resolveReference: async ( + { _id }: { _id: string }, + { models }: IContext, + ) => { + return models.CarCategories.findOne({ _id }); + }, + isRoot: (category: ICarCategoryDocument) => { + return !category.parentId; + }, + + async carCount( + category: ICarCategoryDocument, + _args: any, + { models }: IContext, + ) { + return models.Cars.countDocuments({ + categoryId: category._id, + }); + }, +}; diff --git a/backend/plugins/cars_api/src/modules/cars/graphql/resolvers/mutations/carMutations.ts b/backend/plugins/cars_api/src/modules/cars/graphql/resolvers/mutations/carMutations.ts new file mode 100644 index 0000000000..07fa7e0d32 --- /dev/null +++ b/backend/plugins/cars_api/src/modules/cars/graphql/resolvers/mutations/carMutations.ts @@ -0,0 +1,36 @@ +import { requireLogin, checkPermission } from 'erxes-api-shared/core-modules'; +import { IContext } from '~/connectionResolvers'; +import { ICar, ICarDocument } from '~/modules/cars/@types/car'; + +export const carMutations = { + carsAdd: async (_root: undefined, doc: ICar, { models }: IContext) => { + return await models.Cars.carsAdd({ ...doc }); + }, + + carsEdit: async ( + _root: undefined, + { _id, ...doc }: ICarDocument, + { models }: IContext, + ) => { + return await models.Cars.carsEdit(_id, doc); + }, + + carsRemove: async ( + _root: undefined, + { carIds }: { carIds: string[] }, + { models }: IContext, + ) => { + return await models.Cars.carsRemove(carIds); + }, + + carsMerge: async (_root: undefined, { carIds, carFields }, { models }) => { + return models.Cars.mergeCars(carIds, carFields); + }, +}; + +requireLogin(carMutations, 'manageCars'); + +checkPermission(carMutations, 'carsAdd', 'manageCars'); +checkPermission(carMutations, 'carsEdit', 'manageCars'); +checkPermission(carMutations, 'carsRemove', 'manageCars'); +checkPermission(carMutations, 'carsMerge', 'manageCars'); diff --git a/backend/plugins/cars_api/src/modules/cars/graphql/resolvers/mutations/categoryMutations.ts b/backend/plugins/cars_api/src/modules/cars/graphql/resolvers/mutations/categoryMutations.ts new file mode 100644 index 0000000000..ad84b17fcc --- /dev/null +++ b/backend/plugins/cars_api/src/modules/cars/graphql/resolvers/mutations/categoryMutations.ts @@ -0,0 +1,39 @@ +import { IContext } from '~/connectionResolvers'; +import { requireLogin, checkPermission } from 'erxes-api-shared/core-modules'; + +import { + ICarCategory, + ICarCategoryDocument, +} from '~/modules/cars/@types/category'; + +export const carCategoryMutations = { + carCategoriesAdd: async ( + _root: undefined, + doc: ICarCategory, + { models }: IContext, + ) => { + return await models.CarCategories.carsCategoryAdd({ ...doc }); + }, + + carCategoriesEdit: async ( + _root: undefined, + { _id, ...doc }: ICarCategoryDocument, + { models }: IContext, + ) => { + return await models.CarCategories.carsCategoriesEdit(_id, doc); + }, + + carCategoriesRemove: async ( + _root: undefined, + { _id }: { _id: string }, + { models }: IContext, + ) => { + return await models.CarCategories.carsCategoriesRemove(_id); + }, +}; + +requireLogin(carCategoryMutations, 'manageCars'); + +checkPermission(carCategoryMutations, 'carCategoriesAdd', 'manageCars'); +checkPermission(carCategoryMutations, 'carCategoriesEdit', 'manageCars'); +checkPermission(carCategoryMutations, 'carCategoriesRemove', 'manageCars'); diff --git a/backend/plugins/cars_api/src/modules/cars/graphql/resolvers/queries/carQueries.ts b/backend/plugins/cars_api/src/modules/cars/graphql/resolvers/queries/carQueries.ts new file mode 100644 index 0000000000..922205e63a --- /dev/null +++ b/backend/plugins/cars_api/src/modules/cars/graphql/resolvers/queries/carQueries.ts @@ -0,0 +1,75 @@ +import { IContext } from '~/connectionResolvers'; +import { cursorPaginate } from 'erxes-api-shared/utils'; +import { ICarDocument, ICarParams } from '~/modules/cars/@types/car'; +import { checkPermission, requireLogin } from 'erxes-api-shared/core-modules'; + +export const generateFilter = async (params, commonQuerySelector, models) => { + const { tag } = params; + + const filter: any = commonQuerySelector; + + if (params.categoryId) { + filter.categoryId = params.categoryId; + } + + if (params.searchValue) { + filter.searchText = { $in: [new RegExp(`.*${params.searchValue}.*`, 'i')] }; + } + + if (params.ids) { + filter._id = { $in: params.ids }; + } + + if (tag) { + filter.tagIds = { $in: [tag] }; + } + + return filter; +}; + +export const sortBuilder = (params) => { + const { sortField } = params; + const sortDirection = params.sortDirection || 0; + + if (sortField) { + return { [sortField]: sortDirection }; + } + + return {}; +}; + +export const carQueries = { + //CarDetail + + carDetail: async ( + _root: undefined, + { _id }: { _id: string }, + { models }: IContext, + ) => { + return models.Cars.findOne({ _id }).lean(); + }, + + //Cars list + cars: async ( + _root: undefined, + params: ICarParams, + { commonQuerySelector, models }: IContext, + ) => { + const filter = await generateFilter(params, commonQuerySelector, models); + + return await cursorPaginate({ + model: models.Cars, + params, + query: filter, + }); + }, + + //Cars count + + // carCounts: async () => {} +}; + +requireLogin(carQueries, 'showCars'); + +checkPermission(carQueries, 'cars', 'showCars'); +checkPermission(carQueries, 'carDetail', 'showCars'); diff --git a/backend/plugins/cars_api/src/modules/cars/graphql/resolvers/queries/categoryQueries.ts b/backend/plugins/cars_api/src/modules/cars/graphql/resolvers/queries/categoryQueries.ts new file mode 100644 index 0000000000..79e2da8c38 --- /dev/null +++ b/backend/plugins/cars_api/src/modules/cars/graphql/resolvers/queries/categoryQueries.ts @@ -0,0 +1,34 @@ +import { IContext } from '~/connectionResolvers'; +import { ICarParams } from '~/modules/cars/@types/car'; +import { generateFilter } from './carQueries'; +import { cursorPaginate } from 'erxes-api-shared/utils'; +import { ICarCategoryDocument } from '~/modules/cars/@types/category'; +import { checkPermission, requireLogin } from 'erxes-api-shared/core-modules'; + +export const CarCategoryQueries = { + carCategoryDetail: async ( + _root: undefined, + { _id }: { _id: string }, + { models }: IContext, + ) => { + return models.CarCategories.findOne({ _id }); + }, + carCategories: async ( + _root: undefined, + params: ICarParams, + { commonQuerySelector, models }: IContext, + ) => { + const filter = await generateFilter(params, commonQuerySelector, models); + + return await cursorPaginate({ + model: models.CarCategories, + params, + query: filter, + }); + }, +}; + +requireLogin(CarCategoryQueries, 'showCarCategories'); + +checkPermission(CarCategoryQueries, 'carCategoryDetail', 'showCarCategories'); +checkPermission(CarCategoryQueries, 'carCategories', 'showCarCategories'); diff --git a/backend/plugins/cars_api/src/modules/cars/graphql/schemas/cars.ts b/backend/plugins/cars_api/src/modules/cars/graphql/schemas/cars.ts new file mode 100644 index 0000000000..de2635f019 --- /dev/null +++ b/backend/plugins/cars_api/src/modules/cars/graphql/schemas/cars.ts @@ -0,0 +1,69 @@ +export const types = ` + type Car { + ownerId: String + customer: Customer + mergeIds: [String] + tags: [Tag] + plateNumber: String + vinNumber: String + colorCode: String + category: carCategory + bodyType: String + fuelType: String + gearBox: String + vintageYear: Float + importYear: Float + status: String + description: String + tagIds: [String] + attachment: Attachment + } + + type CarListResponse { + list: [Car], + pageInfo: PageInfo + totalCount: Float, + } + +`; + +const queryParams = ` + page: Int + perPage: Int + tag: String + categoryId: String + ids: [String] + searchValue: String + sortField: String + sortDirection: Int + brand: String +`; + +export const queries = ` + carDetail(_id: String!): Car + cars(${queryParams}): CarListResponse +`; + +const mutationParams = ` + ownerId: String, + mergeIds: [String], + tagIds: [String] + description: String + plateNumber: String + vinNumber: String + colorCode: String + categoryId: String + bodyType: String + fuelType: String + gearBox: String + vintageYear: Float + importYear: Float + attachment: AttachmentInput +`; + +export const mutations = ` + carsAdd(${mutationParams}): Car + carsEdit(_id: String!, ${mutationParams}): Car + carsRemove(carIds: [String!]): JSON + carsMerge(carIds: [String], carFields: JSON): Car +`; diff --git a/backend/plugins/cars_api/src/modules/cars/graphql/schemas/category.ts b/backend/plugins/cars_api/src/modules/cars/graphql/schemas/category.ts new file mode 100644 index 0000000000..fa35f890d5 --- /dev/null +++ b/backend/plugins/cars_api/src/modules/cars/graphql/schemas/category.ts @@ -0,0 +1,38 @@ +export const types = ` + type CarCategory { + name: String + code: String + order: String + parentId: String + description: String + image: Attachment + secondaryImages: Attachment + productCategoryId: String + } + + type carCategoryListResponse { + list: [carCategory], + pageInfo: PageInfo + totalCount: Int,} +`; + +export const queries = ` + carCategoryDetail(_id: String!): CarCategory + carCategories: carCategoryListResponse +`; + +const mutationParams = ` + name: String!, + code: String!, + description: String, + parentId: String, + image: AttachmentInput, + secondaryImages: [AttachmentInput], + productCategoryId: String +`; + +export const mutations = ` + carCategoriesAdd(${mutationParams}): CarCategory + carCategoriesEdit(_id: String!, ${mutationParams}): CarCategory + carCategoriesRemove(_id: String!): CarCategory +`; diff --git a/backend/plugins/cars_api/src/modules/cars/graphql/schemas/extension.ts b/backend/plugins/cars_api/src/modules/cars/graphql/schemas/extension.ts new file mode 100644 index 0000000000..d3213bc0a9 --- /dev/null +++ b/backend/plugins/cars_api/src/modules/cars/graphql/schemas/extension.ts @@ -0,0 +1,14 @@ +export const TypeExtensions = ` + + extend type Company @key(fields: "_id") { + _id: ID! @external + } + + extend type Customer @key(fields: "_id") { + _id: ID! @external + } + + extend type Tag @key(fields: "_id") { + _id: ID! @external + } +`; diff --git a/backend/plugins/cars_api/src/trpc/init-trpc.ts b/backend/plugins/cars_api/src/trpc/init-trpc.ts new file mode 100644 index 0000000000..c52725c8df --- /dev/null +++ b/backend/plugins/cars_api/src/trpc/init-trpc.ts @@ -0,0 +1,21 @@ +import { initTRPC } from '@trpc/server'; +import { escapeRegExp } from 'erxes-api-shared/utils'; +import { z } from 'zod'; + +import { ITRPCContext } from 'erxes-api-shared/utils'; +import { IModels } from '~/connectionResolvers'; + +export type FrontlineTRPCContext = ITRPCContext<{ models: IModels }>; + +const t = initTRPC.context().create(); + +export const carsTrpcRouter = t.router({ + cars: t.router({ + find: t.procedure.input(z.any()).query(async ({ ctx, input }) => { + const { query } = input; + const { models } = ctx; + + return await models.Cars.find(query).lean(); + }), + }), +}); diff --git a/backend/plugins/cars_api/src/trpc/trpcClients.ts b/backend/plugins/cars_api/src/trpc/trpcClients.ts new file mode 100644 index 0000000000..3bf2188687 --- /dev/null +++ b/backend/plugins/cars_api/src/trpc/trpcClients.ts @@ -0,0 +1,18 @@ +import { httpBatchLink, createTRPCUntypedClient } from '@trpc/client'; +import { getPlugin, isEnabled } from 'erxes-api-shared/utils'; + +export const coreTRPCClient = async (): Promise< + ReturnType +> => { + const isCoreEnabled = await isEnabled('core'); + + if (!isCoreEnabled) { + throw new Error('Core plugin is not enabled'); + } + + const core = await getPlugin('core'); + + return createTRPCUntypedClient({ + links: [httpBatchLink({ url: core.address + '/trpc' })], + }); +}; diff --git a/backend/plugins/cars_api/tsconfig.build.json b/backend/plugins/cars_api/tsconfig.build.json new file mode 100644 index 0000000000..52a47e2daa --- /dev/null +++ b/backend/plugins/cars_api/tsconfig.build.json @@ -0,0 +1,22 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "rootDir": ".", + "paths": { + "~/*": [ + "./src/*" + ], + "@/*": [ + "./src/modules/*" + ] + }, + "types": [ + "node" + ] + }, + "exclude": [ + "node_modules", + "dist", + "**/*spec.ts" + ] +} \ No newline at end of file diff --git a/backend/plugins/cars_api/tsconfig.json b/backend/plugins/cars_api/tsconfig.json new file mode 100644 index 0000000000..7076b74596 --- /dev/null +++ b/backend/plugins/cars_api/tsconfig.json @@ -0,0 +1,56 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "baseUrl": ".", + "module": "commonjs", + "declaration": true, + "removeComments": true, + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + "allowSyntheticDefaultImports": true, + "allowUnreachableCode": false, + "esModuleInterop": true, + "target": "es2017", + "sourceMap": true, + "inlineSources": true, + "outDir": "./dist", + "incremental": true, + "skipLibCheck": true, + "strictNullChecks": true, + "alwaysStrict": true, + "noImplicitAny": false, + "strictBindCallApply": false, + "forceConsistentCasingInFileNames": false, + "noFallthroughCasesInSwitch": false, + "resolveJsonModule": true, + "types": [ + "jest", + "node" + ], + "paths": { + "~/*": [ + "./src/*" + ], + "@/*": [ + "./src/modules/*" + ], + "erxes-api-shared/*": [ + "../../erxes-api-shared/src/*" + ] + } + }, + "ts-node": { + "files": true, + "require": [ + "tsconfig-paths/register" + ] + }, + "exclude": [ + "dist", + "frontend/**/*" + ], + "include": [ + "src/**/*.ts", + "src/**/*.tsx" + ] +} \ No newline at end of file diff --git a/et --hard b/et --hard new file mode 100644 index 0000000000..326ef1117e --- /dev/null +++ b/et --hard @@ -0,0 +1,536 @@ +* b674ac4f (HEAD -> car_plugin) Merge branch 'main' of github.com:erxes/erxes-next into car_plugin +|\ +| * b62b9b3f (origin/main, origin/HEAD) feat: settings team-member, select branch department, position and unit modules refactor (#214) +| * 5206c6c9 Merge pull request #215 from erxes/automation-delay +| |\ +| | * 14b2875e chore: remove unused package +| | * 190bb24b fix: coderabbitai suggestions +| | * 07443343 fix: coderabbitai suggestions +| | * c53c24aa fix: coderabbitai suggestion +| | * 15644972 fix: coderabbitai suggestions +| | * 9a51568e chore: cleaning miss imports +| | * e4f21cec fix: send trigger from log when configured contentType in schemawrapper +| | * c5b7182b Merge branch 'main' of github.com:erxes/erxes-next into automation-delay +| | |\ +| | * | 06594310 update(automations): add ability delay using bullmq delay +| | * | 92ba4381 fix: add error handler on matches in get attribution emails +| | * | b9bace7f fix: import icon from tabler +| | * | 120c58c2 chore: cleaning +| | * | 38cd1ff4 Merge branch 'main' of github.com:erxes/erxes-next into automation-updates +| | |\ \ +| | * | | 3e402d20 update: add ability remove segments +| | * | | 0e231a3f fix: optimized some automation files and add ability send email +| | * | | c6f08ade update +| | * | | b353b827 Merge branch 'main' of github.com:erxes/erxes-next into automation-updates +| | |\ \ \ +| | * | | | aa11c501 chore +| | * | | | ba05c77d update +| | * | | | e9d93f7c Merge branch 'main' of github.com:erxes/erxes-next into automation-updates +| | |\ \ \ \ +| | * | | | | 5a2b56bd update(automation): add histories list in detail page +| | * | | | | f2c32781 Merge branch 'main' of github.com:erxes/erxes-next into fix-segment +| | |\ \ \ \ \ +| | * \ \ \ \ \ a08456a1 Merge branch 'main' of github.com:erxes/erxes-next into fix-segment +| | |\ \ \ \ \ \ +| | * | | | | | | 236735d8 (origin/fix-segment) fix: add ability set custom filter in logs recor table +| | * | | | | | | a5d90642 fix(segments): optimize some file name & remove unnecessary code from components +| | * | | | | | | 3ce723ec Merge branch 'main' of github.com:erxes/erxes-next into fix-segment +| | |\ \ \ \ \ \ \ +| | * | | | | | | | 7ce8603e fix: sonar suggestions & remove form label,control from field +| | * | | | | | | | 22ff709b chore: remove unused prop from getSegmentFormDefaultValues +| | * | | | | | | | 23f2acae chore: clean some console.log +| | * | | | | | | | 94749f1e Merge branch 'main' of github.com:erxes/erxes-next into fix-segment +| | |\ \ \ \ \ \ \ \ +| | * | | | | | | | | eaaf1073 fix(segments): optimize folder structure & component names +| * | | | | | | | | | 07a79b3f Merge pull request #220 from erxes/record-table-fix +| |\ \ \ \ \ \ \ \ \ \ +| | * | | | | | | | | | 6172820b refactor cursor +| | * | | | | | | | | | df5034a6 refactor useLocation +| | * | | | | | | | | | 58d1ac87 fix domain +| | * | | | | | | | | | 195d3371 remove integration table +| | * | | | | | | | | | a3b57e96 record table customer refactor +| |/ / / / / / / / / / +| * | | | | | | | | | 0475033c Merge pull request #216 from erxes/ui-modules +| |\ \ \ \ \ \ \ \ \ \ +| | * | | | | | | | | | 33aef325 refactor: remove unused Branches components and queries to streamline the codebase +| | * | | | | | | | | | 4e1329c8 refactor: update hooks and components to use QueryHookOptions for improved type safety +| | * | | | | | | | | | 7eb5e45a feat: enhance customer and company management components with improved hooks and context handling +| | * | | | | | | | | | 22464548 feat: add CompaniesInline and SelectCompanies components for managing company selections +| | * | | | | | | | | | 1470e22f Enhance components and queries for improved functionality +| * | | | | | | | | | | 2ae36e54 Merge pull request #219 from erxes/fix-tailwind +| |\ \ \ \ \ \ \ \ \ \ \ +| | |_|_|_|_|_|_|_|_|_|/ +| |/| | | | | | | | | | +| | * | | | | | | | | | ecaccb5f refactor tailwind config +| * | | | | | | | | | | 5dc6f841 Merge pull request #218 from erxes/fix-tailwind +| |\| | | | | | | | | | +| | * | | | | | | | | | 0dd141dd update icon lib & fix tailwind.config +| |/ / / / / / / / / / +| * | | | | | | | | | abff26cd Merge pull request #205 from erxes/frontline +| |\ \ \ \ \ \ \ \ \ \ +| | |/ / / / / / / / / +| |/| | | | | | | | | +| | * | | | | | | | | 65275117 filter +| | * | | | | | | | | 2d5cc38b remov e company +| | * | | | | | | | | 9bbafbd2 Merge branch 'main' into frontline +| | |\ \ \ \ \ \ \ \ \ +| | |/ / / / / / / / / +| |/| | | | | | | | | +| * | | | | | | | | | 30dc86f4 Merge pull request #211 from erxes/facebook-inbox-fix-notifications +| |\ \ \ \ \ \ \ \ \ \ +| | * | | | | | | | | | 4e95f1ab sendNotifications fix +| | * | | | | | | | | | ce3b36ff sendNotifications function fix api +| * | | | | | | | | | | caf157fb Merge pull request #210 from erxes/facebook-inbox-fix +| |\ \ \ \ \ \ \ \ \ \ \ +| | * \ \ \ \ \ \ \ \ \ \ a862887a Merge branch 'main' into facebook-inbox-fix +| | |\ \ \ \ \ \ \ \ \ \ \ +| | |/ / / / / / / / / / / +| |/| / / / / / / / / / / +| | |/ / / / / / / / / / +| * | | | | | | | | | | d4cd3c79 Merge pull request #209 from erxes/facebook-inbox +| |\ \ \ \ \ \ \ \ \ \ \ +| | |_|_|_|_|_|_|_|_|_|/ +| |/| | | | | | | | | | +| | * | | | | | | | | | 143d873c refactor sendNotifications +| | * | | | | | | | | | ebd2b708 refactor dispatchConversationToService +| | * | | | | | | | | | 864be212 refactor +| | * | | | | | | | | | ae1d3844 fix(facebook): resolve issues with conversation query and createConversation mutation +| |/ / / / / / / / / / +| | * / / / / / / / / 06423557 sendNotifications fix +| |/ / / / / / / / / +| * | | | | | | | | 3dc08d45 chore: remove lucide-react (#207) +| * | | | | | | | | 624ff063 Merge pull request #206 from erxes/brand-resolver +| |\ \ \ \ \ \ \ \ \ +| | * | | | | | | | | 735e79d7 Create brand.ts +| | * | | | | | | | | 2adfae17 Update resolvers.ts +| | * | | | | | | | | 9aeda957 Merge branch 'main' of github.com:erxes/erxes-next +| | |\ \ \ \ \ \ \ \ \ +| | | | * | | | | | | | 18d0421f back +| | | | * | | | | | | | c8459ce8 remove lucide +| | | | * | | | | | | | c9ae143b frontline: filter & styles +| | | | * | | | | | | | fdb93598 query conversationsChangeStatus fix +| | | | * | | | | | | | 3f022fdd mark as read +| | | | * | | | | | | | 19afec2c frontline actions +| | |_|/ / / / / / / / +| |/| | | | | | | | | +* | | | | | | | | | | ec031623 (origin/car_plugin) update +* | | | | | | | | | | c19612c9 schema, constant update +* | | | | | | | | | | f1116d7b car mutation update +* | | | | | | | | | | 3b2ff3ce Merge branch 'main' of github.com:erxes/erxes-next into car_plugin +|\| | | | | | | | | | +| * | | | | | | | | | 83adaaf9 (main) Merge pull request #203 from erxes/conversationMessageAdd-fix +| |\ \ \ \ \ \ \ \ \ \ +| | * | | | | | | | | | d4c87aeb refactor +| | * | | | | | | | | | 0d4c4e14 fix(facebook) conversationMessageAdd mutation fix +| |/ / / / / / / / / / +* | | | | | | | | | | 5a1605f7 update +* | | | | | | | | | | 6c552fb2 update +* | | | | | | | | | | 98ef6a41 update +* | | | | | | | | | | d910cb8c update +* | | | | | | | | | | 48e7c3d1 endpoint update +* | | | | | | | | | | a8b29762 create car plugin +|/ / / / / / / / / / +* | | | | | | | | | c5d4f567 Merge pull request #202 from erxes/frontline +|\ \ \ \ \ \ \ \ \ \ +| * | | | | | | | | | 65f7e07c conversation +* | | | | | | | | | | 783a800d Merge pull request #199 from erxes/fix-imports +|\ \ \ \ \ \ \ \ \ \ \ +| * | | | | | | | | | | a2c3e921 fix Link import +| * | | | | | | | | | | fdd07609 remove string +| |/ / / / / / / / / / +| * / / / / / / / / / ae044c2a fix import paths +|/ / / / / / / / / / +* | | | | | | | | | 8d39ca2d fix: boards query +* | | | | | | | | | b800c28b Merge pull request #197 from erxes/fb-login-url-fix-refactor +|\ \ \ \ \ \ \ \ \ \ +| * | | | | | | | | | bcb5e2f2 refactor +| * | | | | | | | | | 7c2c69ce fix(facebook) import fix +* | | | | | | | | | | 840e5a2e fix: conversations query update +|/ / / / / / / / / / +* | | | | | | | | | 32d427eb Merge pull request #195 from erxes/fb-login-url-fix +|\ \ \ \ \ \ \ \ \ \ +| * | | | | | | | | | a4c1d3a6 fix(facebook) url fix +|/ / / / / / / / / / +* | | | | | | | | | 9661efa4 Merge pull request #193 from erxes/frontline-fixes +|\ \ \ \ \ \ \ \ \ \ +| |_|/ / / / / / / / +|/| | | | | | | | | +| * | | | | | | | | f5efd8b0 integrationKind -> integrationType +* | | | | | | | | | 671b0d34 Merge pull request #192 from erxes/tourism-refactor +|\ \ \ \ \ \ \ \ \ \ +| * | | | | | | | | | e45779f8 refactor +| | |/ / / / / / / / +| |/| | | | | | | | +| * | | | | | | | | 4a9ba810 Merge branch 'main' of github.com:erxes/erxes-next +| |\ \ \ \ \ \ \ \ \ +| * \ \ \ \ \ \ \ \ \ 3a675b9c Merge branch 'main' of github.com:erxes/erxes-next +| |\ \ \ \ \ \ \ \ \ \ +* | \ \ \ \ \ \ \ \ \ \ aa34ac9a Merge pull request #183 from erxes/frontline-fixes +|\ \ \ \ \ \ \ \ \ \ \ \ +| | |_|_|/ / / / / / / / +| |/| | | | | | | | | | +| * | | | | | | | | | | 90c8d3ed fix kinds +| * | | | | | | | | | | b6ea9369 add cursor mode +| * | | | | | | | | | | 8f3eb491 conversation refactor +| * | | | | | | | | | | 960e20b9 fix conversation messages +| |/ / / / / / / / / / +* | | | | | | | | | | 31dc408b fix: frontline fblogin +* | | | | | | | | | | 6997882d feat: remove logs (#191) +* | | | | | | | | | | b1d5012c fix: Accounting transaction remove and some refactors with refetches and table use arrow (#190) +* | | | | | | | | | | c7a1db5a Merge pull request #155 from erxes/pos-plugin +|\ \ \ \ \ \ \ \ \ \ \ +| * \ \ \ \ \ \ \ \ \ \ 704c5326 Merge branch 'main' into pos-plugin +| |\ \ \ \ \ \ \ \ \ \ \ +| |/ / / / / / / / / / / +|/| | | | | | | | | | | +* | | | | | | | | | | | 69931c07 feat: create schema +| |_|_|/ / / / / / / / +|/| | | | | | | | | | +* | | | | | | | | | | 7d1ccd26 feat: add teammember custom resolvers +| |_|/ / / / / / / / +|/| | | | | | | | | +* | | | | | | | | | f0254929 Merge pull request #184 from erxes/string-utils +|\ \ \ \ \ \ \ \ \ \ +| |_|/ / / / / / / / +|/| | | | | | | | | +| * | | | | | | | | ea382001 Update string.ts +| * | | | | | | | | 1dd39a97 fix +|/ / / / / / / / / +* | | | | | | | | 51b69630 settings: file upload +* | | | | | | | | dcc89478 Merge pull request #182 from erxes/frontline-type +|\ \ \ \ \ \ \ \ \ +| |_|_|_|_|_|_|_|/ +|/| | | | | | | | +| * | | | | | | | 2f6166bf integration type +|/ / / / / / / / +* | | | | | | | d2858480 fix: build subscriptions +* | | | | | | | d1079e66 feat: frontline improvment +* | | | | | | | cbe45111 Merge branch 'main' of github.com:erxes/erxes-next +|\ \ \ \ \ \ \ \ +| * | | | | | | | bdee6e89 chore: fix pnpm dev:apis +* | | | | | | | | 2be454e2 fix: start api dev +|/ / / / / / / / +* | | | | | | | c8f6e5f6 fix: fix build +* | | | | | | | bc8aca52 feat: add subsciption +* | | | | | | | f9cdde20 fix(ui): improve FacebookConversationMessages query display +* | | | | | | | 8ff2f547 fix integration value +* | | | | | | | 1c3673b4 fix: Accounting (#178) +* | | | | | | | feb01f8e Merge branch 'main' of github.com:erxes/erxes-next +|\ \ \ \ \ \ \ \ +| * | | | | | | | aade345f fix: fix automations build +| * | | | | | | | 4a934de3 feat: tourism api +| * | | | | | | | 1385276d feat: automations +| * | | | | | | | 272e828b perf: settings +| | |_|_|_|_|_|/ +| |/| | | | | | +| * | | | | | | 8b24832a fix: log service docker-build name +| * | | | | | | 5388f067 fix: services ci names +| * | | | | | | c9c9a1e6 fix: logs-service ci +| * | | | | | | 004aa574 fix: misstype project name of autoamations services +| * | | | | | | 6d8de06b fix:automations ci +| * | | | | | | de27f035 fix: dockerfile of automations-service +| * | | | | | | 8c56112b fix automation dockerfile +| * | | | | | | a9e52c74 debug: add dockerfile in -verbos on automations +| * | | | | | | b33ad260 fix automation dockerfile project.json +| * | | | | | | 082f9e0b fix automation ci +| * | | | | | | 4057fdde fix automation ci +| * | | | | | | 7b630d7c fix automation ci +| * | | | | | | 04255277 Merge branch 'main' of github.com:erxes/erxes-next +| |\ \ \ \ \ \ \ +| | * | | | | | | ff2f175b feat: check undefined of user detail +| | * | | | | | | 938ea83e change react url +| * | | | | | | | 00a2300a add tsconfig.build.json in services +| |/ / / / / / / +| * | | | | | | 1ccdbdec fix: misstypo root automation & logs dockerfile +| * | | | | | | 6ff3b5db Merge branch 'main' of github.com:erxes/erxes-next +| |\ \ \ \ \ \ \ +| | * | | | | | | 9233a3e8 chore: add log for services address +| | * | | | | | | 1f677902 fix: fix gateway health +| * | | | | | | | 0b160658 fix: misstypo automation & logs ci root +| |/ / / / / / / +| * | | | | | | 2ba22acf Merge branch 'main' of github.com:erxes/erxes-next +| |\ \ \ \ \ \ \ +| | * | | | | | | d62f55d0 feat: change services redis address +| * | | | | | | | ca497c53 add ci on services +| |/ / / / / / / +| * / / / / / / 80ad19bf add health in gateway +|/ / / / / / / +* | | | | | | f7608c08 feat: add /health in core-api +* | | | | | | 7e4ae0d2 chore: hide switch organization & invite teammember +* | | | | | | 8b3bf8cc fix: fix padding favorites sidebar +* | | | | | | 2695a6c9 chore: remove time zone & date configs +| |_|_|_|_|/ +|/| | | | | +* | | | | | e3f38a3e facebook +* | | | | | fd681401 Merge pull request #175 from erxes/test-fix +|\ \ \ \ \ \ +| * | | | | | 06c00948 Update table.tsx +| * | | | | | 93943486 feat: update command bars and navigation components for improved functionality and UI consistency +* | | | | | | a08d8e7f Merge pull request #167 from erxes/product +|\ \ \ \ \ \ \ +| |/ / / / / / +|/| | | | | | +| * | | | | | 097bdce7 removes console.log +| * | | | | | 7d043114 refactor: improve barcode handling and UI consistency in product detail form +| * | | | | | b9daa178 update +| * | | | | | 8f83d093 refactor: enhance product form validation and streamline product detail components +| * | | | | | 7d47f514 (origin/product) refactor: simplify file upload component and enhance product detail form error handling +| * | | | | | 976b3f64 feat: implement product management features including form schema, detail view, and category selection +* | | | | | | 15f3560c fix scroll +* | | | | | | 5f58e6bb chore:improve some settings code +* | | | | | | f0b92eb3 fix: ci core ui +* | | | | | | 821e141e feat: add imask +| |_|_|_|_|/ +|/| | | | | +* | | | | | aa501774 feat: imrpove settings +* | | | | | 7d88937a fix: improve Accounting (#173) +* | | | | | eba57981 messenger erxes +* | | | | | 27ef0aa6 Merge pull request #171 from erxes/inbox-docker-image +|\ \ \ \ \ \ +| * | | | | | 7e6588b8 (origin/inbox-docker-image) build docker image inbox +|/ / / / / / +* | | | | | 3f81d037 Merge pull request #170 from erxes/inbox-string-strip-html +|\ \ \ \ \ \ +| * | | | | | 691839b9 (origin/inbox-string-strip-html) inbox-string-strip-html add pnpm +|/ / / / / / +* | | | | | 861e282b Merge pull request #168 from erxes/inbox-backend +|\ \ \ \ \ \ +| * | | | | | 5aeb2340 (origin/inbox-refactor) refactor sendConversationToServices +| * | | | | | 357eedce refactor +| * | | | | | 5052776b refactor +| * | | | | | 498ca735 handleFacebookMessage mutations +| * | | | | | 09990a57 fix(pagination): remove params.cursor from cursorPaginate +| * | | | | | 97110f0d refactor +| * | | | | | 4a1a3815 refactor +| * | | | | | cc1a9817 refactor +| * | | | | | fbd7c4b3 inbox refactor +* | | | | | | 2a36dea4 Merge pull request #169 from erxes/inbox +|\ \ \ \ \ \ \ +| * | | | | | | 3d6a3975 fix ai suggests +| * | | | | | | 0434422c remove space +| * | | | | | | 389d0f0a remove backend changes +| * | | | | | | 407a05a5 Merge branch 'main' into inbox +| |\ \ \ \ \ \ \ +| |/ / / / / / / +|/| | | | | | | +* | | | | | | | 3359356b Merge pull request #154 from erxes/contacts-command-bar +|\ \ \ \ \ \ \ \ +| |_|_|/ / / / / +|/| | | | | | | +| * | | | | | | 8564c77f (origin/contacts-command-bar) refactor: ai recommendation +| * | | | | | | b2e488f3 feat: Refactor customer selection components and improve context management +| * | | | | | | 58d0c3fc Merge branch 'main' of github.com:erxes/erxes-next into contacts-command-bar +| |\ \ \ \ \ \ \ +| |/ / / / / / / +|/| | | | | | | +* | | | | | | | 9082de65 fix: accounting inv_income begin and some refactors (#166) +| |_|/ / / / / +|/| | | | | | +| * | | | | | 57a2a293 Merge branch 'main' of github.com:erxes/erxes-next into contacts-command-bar +| |\ \ \ \ \ \ +| |/ / / / / / +|/| | | | | | +* | | | | | | 62d9fbcf Merge branch 'main' of github.com:erxes/erxes-next +|\ \ \ \ \ \ \ +| * | | | | | | e1dffbdc move sales (#164) +* | | | | | | | 67be9022 Update pnpm-lock.yaml +|/ / / / / / / +* | | | | | | b54fe104 Merge branch 'main' of github.com:erxes/erxes-next +|\ \ \ \ \ \ \ +| |_|_|_|_|_|/ +|/| | | | | | +| * | | | | | 0376ae69 core initTRPC +| * | | | | | 38e4a6a3 inbox trpc fix +| * | | | | | f633b7c2 feat: inbox trpc +| * | | | | | 833334e0 fix: start plugin +| * | | | | | d809d379 feat: add express router +|/ / / / / / +* | | | | | f43b74fb fix: improve Accounting (#162) +* | | | | | 331fc717 fix: trpc context +* | | | | | 0a5d1eea Merge pull request #161 from erxes/log-api +|\ \ \ \ \ \ +| * | | | | | c40c063c (origin/ui_automations_logs, origin/log-api) fix: gitignore logs +| * | | | | | c72b3755 Merge branch 'main' of github.com:erxes/erxes-next into ui_automations_logs +| |\ \ \ \ \ \ +| |/ / / / / / +|/| | | | | | +* | | | | | | 2c36a45c fix: fix some schemas +| * | | | | | 93c80290 fix:miss typo +| * | | | | | 4161ff1f Merge branch 'main' of github.com:erxes/erxes-next into ui_automations_logs +| |\ \ \ \ \ \ +| * | | | | | | 33f9b121 fix: optimize logs & automations ui +| * | | | | | | 095f8c6c fix: coderabbitai suggestions +| * | | | | | | 77fa4a94 fix: automation constant miss typo +| * | | | | | | e74b6375 Merge branch 'main' of github.com:erxes/erxes-next into services +| |\ \ \ \ \ \ \ +| * | | | | | | | 5441adef update: optimize logs&automation api folder strcuture +| * | | | | | | | 5f355943 Merge branch 'afterMutations' of github.com:erxes/erxes-next into services +| |\ \ \ \ \ \ \ \ +| | * | | | | | | | d9814588 fix: store log after process when error & pass processId propertly +| | * | | | | | | | b6cf40d0 fix: coderabbitai suggestions +| | * | | | | | | | 7901b851 fix: miss typo afterAPIRequest +| | * | | | | | | | 95d5b633 fix: clear active change stream when service down +| | * | | | | | | | ae5344f4 chore: remove unused object +| | * | | | | | | | 96f9dcbc feat: add ability recieve after process in plugin +| | | | | * | | | | 8167adc5 Merge branch 'main' of github.com:erxes/erxes-next into contacts-command-bar +| | | | | |\ \ \ \ \ +| |_|_|_|_|/ / / / / +|/| | | | | | | | | +* | | | | | | | | | 282627dd chore: fix logs +* | | | | | | | | | 961225b7 add cursorMode to cursor pagination +| | | | | * | | | | 3cf9397a Merge branch 'main' of github.com:erxes/erxes-next into contacts-command-bar +| | | | | |\ \ \ \ \ +| |_|_|_|_|/ / / / / +|/| | | | | | | | | +* | | | | | | | | | b71f647c feat: ui automations logs (#159) +| |_|_|/ / / / / / +|/| | | | | | | | +* | | | | | | | | 3e4910af feat: automations & logs services +* | | | | | | | | f74edb02 feat: after mutations +* | | | | | | | | 8adcb9d9 Accounting select account and single details journals update (#158) +| | | | * | | | | bfdf48da Merge branch 'main' of github.com:erxes/erxes-next into contacts-command-bar +| | | | |\ \ \ \ \ +| | | | |/ / / / / +| | | |/| | | | | +| | | | * | | | | f5830eaf feat: Add Companies Merge functionality with UI components +| | | | | * | | | 10f282e2 add facebook integratio +| | | | | * | | | 5a9180f3 refactor: update inbox integration routes, enhance Facebook integration components, and improve GraphQL queries +| | | | | * | | | 958e88c5 refactor: update Facebook GraphQL queries, add IntegrationSteps component, and create Facebook integration components +| | | | | * | | | f59e688d refactor: enhance NavigationButton component logic and improve Sidebar styling; add new integration components and update GraphQL queries +| |_|_|_|/ / / / +|/| | | | | | | +* | | | | | | | 81e1df02 refactor: update BrandField to use single value and integrate SelectBrand component; add brand selection functionality +| |_|/ / / / / +|/| | | | | | +* | | | | | | 5bb5d35c fix: members inline +* | | | | | | d98eac9c merge +* | | | | | | 9ab27754 must customer +* | | | | | | 872851e1 continue +* | | | | | | b11db206 fix: deb fund forms +* | | | | | | b41ac43d Merge pull request #153 from erxes/accounting +|\ \ \ \ \ \ \ +| * | | | | | | 8cf925d8 refactor: update AccountingTopbar to return null and enhance CurrencyForm logic +| * | | | | | | 8a2faaab currency check +| |/ / / / / / +| | | | * | | b250f5e1 update +| | | | * | | 5de5ef46 refactor: enhance type safety and consistency in POS components, streamline state management, and optimize imports across various modules +| | | | * | | 5d01eaed refactor: correct spelling in constants, enhance POS form schemas with new options, and replace Select components with Input for better user experience +| | | | * | | 727d3cb6 refactor: enhance type safety in POS components by updating props and improving form handling, streamline state management, and optimize imports +| | | | * | | 84d540b8 (origin/pos-plugin) refactor: enhance POS form schemas with optional departmentId, improve type safety in appearance and finance components, and streamline form handling +| | | | * | | 0f1a572c refactor: streamline POS component props, enhance type safety, and improve form handling with read-only states +| | | | * | | 93fb4843 refactor: align POS form schemas with backend structure, enhance payment method handling, and improve component props for better type safety +| | | | * | | 1b6e85c0 refactor: update component imports, enhance POS forms with read-only states, and remove unused components +| | | | * | | 675d2eb1 feat: add 'pos' translation and export stepper component +| |_|_|/ / / +|/| | | | | +* | | | | | 9b4af6a7 fix: pos resolvers +* | | | | | 600ec37f docModifier delete +| |_|/ / / +|/| | | | +* | | | | 6f973572 Merge pull request #147 from erxes/inbox-structure +|\ \ \ \ \ +| |/ / / / +|/| | | | +| * | | | 68c17855 (origin/inbox-structure) chore: update dependencies in pnpm-lock.yaml and refactor RecordTableCursor, MessageItem, and FbPostConversationDetail components for improved functionality +| * | | | 337b5b48 Merge branch 'main' into inbox-structure +| |\ \ \ \ +| |/ / / / +|/| | | | +* | | | | 8b3761d5 feat: tags settings +| |/ / / +|/| | | +* | | | 71b30b5c fix: calc tax +* | | | 42725193 fix: ctaxform +* | | | c08d87cd allow removing multiple brands +* | | | 6c74f94a chore: remove password regex from login form +* | | | 112323c9 feat: widgets +* | | | 4463ccb3 feat: widgets sidebar for contacts +| * | | 83e8e9a8 merge +| * | | 2ceff1fc refactor: update components and hooks for improved functionality and structure in inbox and integrations modules +|/ / / +* | | 0907e038 feat: file handler +* | | cbb79220 feat: brands settings (#145) +* | | 36cd25dc fix: vat form finish +* | | 92003cb9 fix(frontline_ui): close unclosed JSX tag in InboxIndexPage to resolve build error +* | | 6be92af2 feat: add create plugin script +| |/ +|/| +* | 66fbb99b Merge pull request #138 from erxes/category +|\ \ +| * | 260677a4 (origin/category) feat: add cancel functionality to AddProductCategory form and refactor category detail handling +| * | 2e01e0ed feat: add product category mutations and queries, enhance product category management UI +| |/ +* / acaff0a1 fix: vatForm up +|/ +* d4cebfd5 Merge branch 'main' of github.com:erxes/erxes-next +|\ +| * dc64252a refactor: optimize imports and simplify component structure in segments module +| * 40862a2d Merge branches 'main' and 'main' of github.com:erxes/erxes-next +| |\ +| | * 90dd3789 fat: get PORT from env +| * | e3b6fa4d fix: use trpc context type from erxes-api-shared +| |/ +| * 18e70e5c fix build +| * 0092704b feat: inbox channels +| * 2b569012 remove duplicated sendTrpmessage +| * f238d6f9 feat: event logs & automation & segments & form(Fields) (#91) +| * 1c7063a1 merge +| |\ +| | * 138c116a refactor: streamline TeamMember component structure and enhance filter functionality +| | * 6465342d refactor: update ProductsSettings route and clean up GeneralSettings component +| | * 736cdbf3 add frontline api ci +| | * 3ac78cb6 feat: add ProductsSettings route and enhance TagsRecordTable with sorted tags +| | * 36e40757 update +| | * 69eb88f3 chore: add frontline ci & dockerfile +| | * 964f5c56 chore: add frontline ci & dockerfile +| | * 8153c605 move document & add tag meta to core (#135) +| | * fb54edec pos ci check 1 +| | * 97cd4697 accounting ci check 1 +| | * 1842e5fe accounting ci check +| | * 964256e9 accounting ci check +| | * 7af69245 fix: pos_api accounting_api CI +| | * 97288b18 chore: update project.json to include asset path for frontline_ui +| | * 44e06c4a refactor: replace FilterDropdown with FilterConversations in ConversationFilter component +| * | bafea30e add scripts +| |/ +| * 240670f3 Settings > Structure (#131) +| * 5530d95f permission +| * da0af568 fix: add pos_api +| * e12f5c88 chore: products type with cache +| * b3354b30 chore: little +| * 03a045be chore: trpc default method is query +| * 0c73f29d fix: trpc action consumer +| * 6909686c Merge pull request #132 from erxes/setting-cursor +| |\ +| | * 4fb014f7 (origin/setting-cursor) refactor +| | * 7c03d006 feat(setting): implement cursor-based pagination in UI and API +| |/ +| * e2d20bfc feat(facebook): add core posting and commenting helpers - Implement facebookPostHelper.ts - Create facebookCommentHelper.ts - Integrate with existing controllers (#120) +| * 2c49c4b8 fix inbox conversation result +| * 0751f85a move permission & add permission to contact +| * 2cb46bcc refactor(routes): update ContactsRoutes and SettingsRoutes for improved navigation +| * 2fad05af Merge pull request #130 from erxes/inbox-query-fix +| |\ +| | * 1d347c85 (origin/inbox-query-fix) fix(inbox) conversation query +| |/ +| * 26dec164 feat(inbox): enhance GET_CONVERSATIONS query with cursor parameters and improved structure +| * db76d6c6 Merge pull request #129 from erxes/inbox-cursor-fix +|/| +| * ee745063 (origin/inbox-cursor-fix) refactor +| * 5de4bd75 feat(inbox) cursor arguments +|/ +* 0cd31089 feat: implement tags management in settings +* fc3bc516 chore: accounting import +* 7efac012 chore: accounting definitions +* 827b85f7 Update pnpm-lock.yaml +* 163d3613 Merge branch 'main' of github.com:erxes/erxes-next +|\ +| * 8023cd1a chore: +| * 90da25df feat: enhance ContactsPageEffect with dynamic hotkey scope management +| * de376f43 refactor: streamline hotkey scopes and enhance contacts routing +| * f0e6af0d feat: implement session key for customers filter and record table +| * 32e65980 feat: add @tanstack/react-virtual dependency and enhance contacts routing +| * ce151713 chore: accounting tbalance and taxForm start +| * 7089af8d chore: little +| * 85b7b166 chore: little +| * 6fccfde6 chore: accounting refactor diff --git a/frontend/plugins/cars_ui/eslint.config.js b/frontend/plugins/cars_ui/eslint.config.js new file mode 100644 index 0000000000..b3387659a3 --- /dev/null +++ b/frontend/plugins/cars_ui/eslint.config.js @@ -0,0 +1,11 @@ +const nx = require('@nx/eslint-plugin'); +const baseConfig = require('../../../eslint.config.js'); + +module.exports = [ + ...baseConfig, + ...nx.configs['flat/react'], + { + files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'], + rules: {}, + }, +]; diff --git a/frontend/plugins/cars_ui/jest.config.ts b/frontend/plugins/cars_ui/jest.config.ts new file mode 100644 index 0000000000..3b00cbb946 --- /dev/null +++ b/frontend/plugins/cars_ui/jest.config.ts @@ -0,0 +1,11 @@ +/* eslint-disable */ +export default { + displayName: 'cars-ui', + preset: '../../../jest.preset.js', + testEnvironment: 'node', + transform: { + '^.+\\.[tj]s$': ['ts-jest', { tsconfig: '/tsconfig.spec.json' }], + }, + moduleFileExtensions: ['ts', 'js', 'html'], + coverageDirectory: '../../coverage/plugins/cars_ui', +}; diff --git a/frontend/plugins/cars_ui/module-federation.config.ts b/frontend/plugins/cars_ui/module-federation.config.ts new file mode 100644 index 0000000000..0178a033d1 --- /dev/null +++ b/frontend/plugins/cars_ui/module-federation.config.ts @@ -0,0 +1,34 @@ +import { ModuleFederationConfig } from '@nx/rspack/module-federation'; + +const coreLibraries = new Set([ + 'react', + 'react-dom', + 'react-router', + 'react-router-dom', + 'erxes-ui', + '@apollo/client', + 'jotai', + 'ui-modules', + 'react-i18next', +]); + +const config: ModuleFederationConfig = { + name: 'cars_ui', + exposes: { + './config': './src/config.ts', + './module': './src/modules/module/Main.tsx', + './moduleSettings': './src/modules/module/Settings.tsx', + './widgets': './src/widgets/Widgets.tsx', + }, + + shared: (libraryName, defaultConfig) => { + if (coreLibraries.has(libraryName)) { + return defaultConfig; + } + + // Returning false means the library is not shared. + return false; + }, +}; + +export default config; diff --git a/frontend/plugins/cars_ui/project.json b/frontend/plugins/cars_ui/project.json new file mode 100644 index 0000000000..328da477ad --- /dev/null +++ b/frontend/plugins/cars_ui/project.json @@ -0,0 +1,65 @@ +{ + "name": "cars_ui", + "$schema": "../../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "frontend/plugins/cars_ui/src", + "projectType": "application", + "tags": [], + "targets": { + "build": { + "executor": "@nx/rspack:rspack", + "outputs": [ + "{options.outputPath}" + ], + "defaultConfiguration": "production", + "options": { + "target": "web", + "outputPath": "dist/frontend/plugins/cars_ui", + "main": "frontend/plugins/cars_ui/src/main.ts", + "tsConfig": "frontend/plugins/cars_ui/tsconfig.app.json", + "rspackConfig": "frontend/plugins/cars_ui/rspack.config.ts", + "assets": [ + "frontend/plugins/cars_ui/src/assets" + ] + }, + "configurations": { + "development": { + "mode": "development" + }, + "production": { + "mode": "production", + "optimization": true, + "sourceMap": false, + "rspackConfig": "frontend/plugins/cars_ui/rspack.config.prod.ts" + } + } + }, + "serve": { + "executor": "@nx/rspack:module-federation-dev-server", + "options": { + "buildTarget": "cars_ui:build:development", + "port": 3005 + }, + "configurations": { + "development": {}, + "production": { + "buildTarget": "cars_ui:build:production" + } + } + }, + "serve-static": { + "executor": "@nx/rspack:module-federation-static-server", + "defaultConfiguration": "production", + "options": { + "serveTarget": "cars_ui:serve" + }, + "configurations": { + "development": { + "serveTarget": "cars_ui:serve:development" + }, + "production": { + "serveTarget": "cars_ui:serve:production" + } + } + } + } +} \ No newline at end of file diff --git a/frontend/plugins/cars_ui/rspack.config.prod.ts b/frontend/plugins/cars_ui/rspack.config.prod.ts new file mode 100644 index 0000000000..59435a495f --- /dev/null +++ b/frontend/plugins/cars_ui/rspack.config.prod.ts @@ -0,0 +1 @@ +export default require('./rspack.config'); \ No newline at end of file diff --git a/frontend/plugins/cars_ui/rspack.config.ts b/frontend/plugins/cars_ui/rspack.config.ts new file mode 100644 index 0000000000..d718022abe --- /dev/null +++ b/frontend/plugins/cars_ui/rspack.config.ts @@ -0,0 +1,14 @@ +import { composePlugins, withNx, withReact } from '@nx/rspack'; +import { withModuleFederation } from '@nx/rspack/module-federation'; + +import baseConfig from './module-federation.config'; + +const config = { + ...baseConfig, +}; + +export default composePlugins( + withNx(), + withReact(), + withModuleFederation(config, { dts: false }), +); diff --git a/frontend/plugins/cars_ui/src/assets/README.md b/frontend/plugins/cars_ui/src/assets/README.md new file mode 100644 index 0000000000..a5e88d0360 --- /dev/null +++ b/frontend/plugins/cars_ui/src/assets/README.md @@ -0,0 +1,24 @@ +# Assets Directory + +This directory contains static assets used by the plugin. + +## Contents + +- `example-icon.svg`: Example icon in SVG format +- `example-image.svg`: Example placeholder image in SVG format + +## Usage + +Import assets in your components like this: + +```tsx +import exampleIcon from '~/assets/example-icon.svg'; +import exampleImage from '~/assets/example-image.svg'; +``` + +## Best Practices + +1. Use SVG format for icons and simple graphics +2. Optimize images before adding them to the assets folder +3. Keep file names descriptive and in kebab-case +4. Document any new assets added to this directory diff --git a/frontend/plugins/cars_ui/src/assets/example-icon.svg b/frontend/plugins/cars_ui/src/assets/example-icon.svg new file mode 100644 index 0000000000..bca4713f79 --- /dev/null +++ b/frontend/plugins/cars_ui/src/assets/example-icon.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/frontend/plugins/cars_ui/src/assets/example-image.svg b/frontend/plugins/cars_ui/src/assets/example-image.svg new file mode 100644 index 0000000000..a62ab22c39 --- /dev/null +++ b/frontend/plugins/cars_ui/src/assets/example-image.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/frontend/plugins/cars_ui/src/bootstrap.tsx b/frontend/plugins/cars_ui/src/bootstrap.tsx new file mode 100644 index 0000000000..32482de416 --- /dev/null +++ b/frontend/plugins/cars_ui/src/bootstrap.tsx @@ -0,0 +1,11 @@ +import { StrictMode } from 'react'; +import * as ReactDOM from 'react-dom/client'; + +const root = ReactDOM.createRoot( + document.getElementById('root') as HTMLElement, +); +root.render( + +
App
+
, +); diff --git a/frontend/plugins/cars_ui/src/config.ts b/frontend/plugins/cars_ui/src/config.ts new file mode 100644 index 0000000000..ff6d354436 --- /dev/null +++ b/frontend/plugins/cars_ui/src/config.ts @@ -0,0 +1,18 @@ +import { IconSandbox } from '@tabler/icons-react'; + + +import { IUIConfig } from 'erxes-ui/types'; + +export const CONFIG: IUIConfig = { + name: 'cars', + icon: IconSandbox, + modules: [ + { + name: 'module', + icon: IconSandbox, + path: 'module', + hasSettings: true, + hasWidgets: true, + }, + ], +}; diff --git a/frontend/plugins/cars_ui/src/index.html b/frontend/plugins/cars_ui/src/index.html new file mode 100644 index 0000000000..a18d2c4955 --- /dev/null +++ b/frontend/plugins/cars_ui/src/index.html @@ -0,0 +1,14 @@ + + + + + cars + + + + + + +
+ + diff --git a/frontend/plugins/cars_ui/src/main.ts b/frontend/plugins/cars_ui/src/main.ts new file mode 100644 index 0000000000..137c64f9f4 --- /dev/null +++ b/frontend/plugins/cars_ui/src/main.ts @@ -0,0 +1 @@ +import('./bootstrap'); \ No newline at end of file diff --git a/frontend/plugins/cars_ui/src/modules/module/Main.tsx b/frontend/plugins/cars_ui/src/modules/module/Main.tsx new file mode 100644 index 0000000000..6aff913697 --- /dev/null +++ b/frontend/plugins/cars_ui/src/modules/module/Main.tsx @@ -0,0 +1,20 @@ +import { lazy, Suspense } from 'react'; +import { Route, Routes } from 'react-router'; + +const IndexPage = lazy(() => + import('~/pages/module/IndexPage').then((module) => ({ + default: module.IndexPage, + })), +); + +const moduleMain = () => { + return ( + }> + + } /> + + + ); +}; + +export default moduleMain; diff --git a/frontend/plugins/cars_ui/src/modules/module/Settings.tsx b/frontend/plugins/cars_ui/src/modules/module/Settings.tsx new file mode 100644 index 0000000000..93406c8298 --- /dev/null +++ b/frontend/plugins/cars_ui/src/modules/module/Settings.tsx @@ -0,0 +1,9 @@ +const moduleSettings = () => { + return ( +
+

module Settings

+
+ ); +}; + +export default moduleSettings; diff --git a/frontend/plugins/cars_ui/src/pages/module/IndexPage.tsx b/frontend/plugins/cars_ui/src/pages/module/IndexPage.tsx new file mode 100644 index 0000000000..9321c7bd90 --- /dev/null +++ b/frontend/plugins/cars_ui/src/pages/module/IndexPage.tsx @@ -0,0 +1,49 @@ +import { + IconCaretDownFilled, + IconSandbox, + IconSettings, +} from '@tabler/icons-react'; +import { Breadcrumb, Button, Separator } from 'erxes-ui'; +import { PageHeader } from 'ui-modules'; +import { Link } from 'react-router-dom'; + +export const IndexPage = () => { + return ( +
+ + + + + + + + + + + + + + + + + +
+
+

module

+
+
+
+ ); +}; diff --git a/frontend/plugins/cars_ui/src/widgets/Widgets.tsx b/frontend/plugins/cars_ui/src/widgets/Widgets.tsx new file mode 100644 index 0000000000..41fc867f32 --- /dev/null +++ b/frontend/plugins/cars_ui/src/widgets/Widgets.tsx @@ -0,0 +1,13 @@ +export const Widgets = ({ + module, + contentId, + contentType, +}: { + module: any; + contentId: string; + contentType: string; +}) => { + return
module Widget
; +}; + +export default Widgets; diff --git a/frontend/plugins/cars_ui/tsconfig.app.json b/frontend/plugins/cars_ui/tsconfig.app.json new file mode 100644 index 0000000000..e327240abd --- /dev/null +++ b/frontend/plugins/cars_ui/tsconfig.app.json @@ -0,0 +1,28 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../../dist/out-tsc", + "types": [ + "node", + "@nx/react/typings/cssmodule.d.ts", + "@nx/react/typings/image.d.ts" + ] + }, + "exclude": [ + "jest.config.ts", + "src/**/*.spec.ts", + "src/**/*.test.ts", + "src/**/*.spec.tsx", + "src/**/*.test.tsx", + "src/**/*.spec.js", + "src/**/*.test.js", + "src/**/*.spec.jsx", + "src/**/*.test.jsx" + ], + "include": [ + "src/**/*.js", + "src/**/*.jsx", + "src/**/*.ts", + "src/**/*.tsx" + ] +} \ No newline at end of file diff --git a/frontend/plugins/cars_ui/tsconfig.json b/frontend/plugins/cars_ui/tsconfig.json new file mode 100644 index 0000000000..b391a9b389 --- /dev/null +++ b/frontend/plugins/cars_ui/tsconfig.json @@ -0,0 +1,46 @@ +{ + "compilerOptions": { + "jsx": "react-jsx", + "allowJs": false, + "esModuleInterop": false, + "allowSyntheticDefaultImports": true, + "strict": true, + "paths": { + "erxes-ui/*": [ + "frontend/libs/erxes-ui/src/*" + ], + "erxes-ui": [ + "frontend/libs/erxes-ui/src" + ], + "~/*": [ + "frontend/plugins/cars_ui/src/*" + ], + "~": [ + "frontend/plugins/cars_ui/src" + ], + "@/*": [ + "frontend/plugins/cars_ui/src/modules/*" + ], + "@": [ + "frontend/plugins/cars_ui/src/modules" + ], + "ui-modules/*": [ + "frontend/libs/ui-modules/src/*" + ], + "ui-modules": [ + "frontend/libs/ui-modules/src" + ] + } + }, + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.app.json" + }, + { + "path": "./tsconfig.spec.json" + } + ], + "extends": "../../../tsconfig.base.json" +} \ No newline at end of file diff --git a/frontend/plugins/cars_ui/tsconfig.spec.json b/frontend/plugins/cars_ui/tsconfig.spec.json new file mode 100644 index 0000000000..5073ef03e6 --- /dev/null +++ b/frontend/plugins/cars_ui/tsconfig.spec.json @@ -0,0 +1,27 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../../dist/out-tsc", + "module": "commonjs", + "moduleResolution": "node10", + "jsx": "react-jsx", + "types": [ + "jest", + "node", + "@nx/react/typings/cssmodule.d.ts", + "@nx/react/typings/image.d.ts" + ] + }, + "include": [ + "jest.config.ts", + "src/**/*.test.ts", + "src/**/*.spec.ts", + "src/**/*.test.tsx", + "src/**/*.spec.tsx", + "src/**/*.test.js", + "src/**/*.spec.js", + "src/**/*.test.jsx", + "src/**/*.spec.jsx", + "src/**/*.d.ts" + ] +} \ No newline at end of file diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f120f4e243..ccf824df34 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -779,11 +779,17 @@ importers: specifier: workspace:^ version: link:../../erxes-api-shared +<<<<<<< HEAD + backend/plugins/cars_api: +======= backend/plugins/content_api: +>>>>>>> b9fcc6e94fe8d9d59da2cc9e48939d1e201bce67 dependencies: erxes-api-shared: specifier: workspace:^ version: link:../../erxes-api-shared +<<<<<<< HEAD +======= html-to-text: specifier: ^8.2.1 version: 8.2.1 @@ -796,6 +802,7 @@ importers: tmp: specifier: ^0.2.3 version: 0.2.3 +>>>>>>> b9fcc6e94fe8d9d59da2cc9e48939d1e201bce67 backend/plugins/frontline_api: dependencies: