mirror of
https://github.com/hexastack/hexabot
synced 2024-11-24 04:53:41 +00:00
feat: add language crud
This commit is contained in:
parent
614766c246
commit
10f36c2d48
151
api/src/i18n/controllers/language.controller.ts
Normal file
151
api/src/i18n/controllers/language.controller.ts
Normal file
@ -0,0 +1,151 @@
|
||||
/*
|
||||
* Copyright © 2024 Hexastack. All rights reserved.
|
||||
*
|
||||
* Licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms:
|
||||
* 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission.
|
||||
* 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file).
|
||||
* 3. SaaS Restriction: This software, or any derivative of it, may not be used to offer a competing product or service (SaaS) without prior written consent from Hexastack. Offering the software as a service or using it in a commercial cloud environment without express permission is strictly prohibited.
|
||||
*/
|
||||
|
||||
import {
|
||||
BadRequestException,
|
||||
Body,
|
||||
Controller,
|
||||
Delete,
|
||||
Get,
|
||||
HttpCode,
|
||||
NotFoundException,
|
||||
Param,
|
||||
Patch,
|
||||
Post,
|
||||
Query,
|
||||
UseInterceptors,
|
||||
} from '@nestjs/common';
|
||||
import { CsrfCheck } from '@tekuconcept/nestjs-csrf';
|
||||
import { TFilterQuery } from 'mongoose';
|
||||
|
||||
import { CsrfInterceptor } from '@/interceptors/csrf.interceptor';
|
||||
import { LoggerService } from '@/logger/logger.service';
|
||||
import { BaseController } from '@/utils/generics/base-controller';
|
||||
import { DeleteResult } from '@/utils/generics/base-repository';
|
||||
import { PageQueryDto } from '@/utils/pagination/pagination-query.dto';
|
||||
import { PageQueryPipe } from '@/utils/pagination/pagination-query.pipe';
|
||||
import { SearchFilterPipe } from '@/utils/pipes/search-filter.pipe';
|
||||
|
||||
import { LanguageCreateDto, LanguageUpdateDto } from '../dto/language.dto';
|
||||
import { Language } from '../schemas/language.schema';
|
||||
import { LanguageService } from '../services/language.service';
|
||||
|
||||
@UseInterceptors(CsrfInterceptor)
|
||||
@Controller('language')
|
||||
export class LanguageController extends BaseController<Language> {
|
||||
constructor(
|
||||
private readonly languageService: LanguageService,
|
||||
private readonly logger: LoggerService,
|
||||
) {
|
||||
super(languageService);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a paginated list of categories based on provided filters and pagination settings.
|
||||
* @param pageQuery - The pagination settings.
|
||||
* @param filters - The filters to apply to the language search.
|
||||
* @returns A Promise that resolves to a paginated list of categories.
|
||||
*/
|
||||
@Get()
|
||||
async findPage(
|
||||
@Query(PageQueryPipe) pageQuery: PageQueryDto<Language>,
|
||||
@Query(new SearchFilterPipe<Language>({ allowedFields: ['title', 'code'] }))
|
||||
filters: TFilterQuery<Language>,
|
||||
) {
|
||||
return await this.languageService.findPage(filters, pageQuery);
|
||||
}
|
||||
|
||||
/**
|
||||
* Counts the filtered number of categories.
|
||||
* @returns A promise that resolves to an object representing the filtered number of categories.
|
||||
*/
|
||||
@Get('count')
|
||||
async filterCount(
|
||||
@Query(
|
||||
new SearchFilterPipe<Language>({
|
||||
allowedFields: ['title', 'code'],
|
||||
}),
|
||||
)
|
||||
filters?: TFilterQuery<Language>,
|
||||
) {
|
||||
return await this.count(filters);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a language by its ID.
|
||||
* @param id - The ID of the language to find.
|
||||
* @returns A Promise that resolves to the found language.
|
||||
*/
|
||||
@Get(':id')
|
||||
async findOne(@Param('id') id: string): Promise<Language> {
|
||||
const doc = await this.languageService.findOne(id);
|
||||
if (!doc) {
|
||||
this.logger.warn(`Unable to find Language by id ${id}`);
|
||||
throw new NotFoundException(`Language with ID ${id} not found`);
|
||||
}
|
||||
return doc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new language.
|
||||
* @param language - The data of the language to be created.
|
||||
* @returns A Promise that resolves to the created language.
|
||||
*/
|
||||
@CsrfCheck(true)
|
||||
@Post()
|
||||
async create(@Body() language: LanguageCreateDto): Promise<Language> {
|
||||
return await this.languageService.create(language);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates an existing language.
|
||||
* @param id - The ID of the language to be updated.
|
||||
* @param languageUpdate - The updated data for the language.
|
||||
* @returns A Promise that resolves to the updated language.
|
||||
*/
|
||||
@CsrfCheck(true)
|
||||
@Patch(':id')
|
||||
async updateOne(
|
||||
@Param('id') id: string,
|
||||
@Body() languageUpdate: LanguageUpdateDto,
|
||||
): Promise<Language> {
|
||||
if ('default' in languageUpdate) {
|
||||
if (languageUpdate.default) {
|
||||
// A new default language is define, make sure that only one is marked as default
|
||||
await this.languageService.updateMany({}, { default: false });
|
||||
} else {
|
||||
throw new BadRequestException('Should not be able to disable default');
|
||||
}
|
||||
}
|
||||
|
||||
const result = await this.languageService.updateOne(id, languageUpdate);
|
||||
if (!result) {
|
||||
this.logger.warn(`Unable to update Language by id ${id}`);
|
||||
throw new NotFoundException(`Language with ID ${id} not found`);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a language by its ID.
|
||||
* @param id - The ID of the language to be deleted.
|
||||
* @returns A Promise that resolves to the deletion result.
|
||||
*/
|
||||
@CsrfCheck(true)
|
||||
@Delete(':id')
|
||||
@HttpCode(204)
|
||||
async deleteOne(@Param('id') id: string): Promise<DeleteResult> {
|
||||
const result = await this.languageService.deleteOne(id);
|
||||
if (result.deletedCount === 0) {
|
||||
this.logger.warn(`Unable to delete Language by id ${id}`);
|
||||
throw new NotFoundException(`Language with ID ${id} not found`);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
31
api/src/i18n/dto/language.dto.ts
Normal file
31
api/src/i18n/dto/language.dto.ts
Normal file
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright © 2024 Hexastack. All rights reserved.
|
||||
*
|
||||
* Licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms:
|
||||
* 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission.
|
||||
* 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file).
|
||||
* 3. SaaS Restriction: This software, or any derivative of it, may not be used to offer a competing product or service (SaaS) without prior written consent from Hexastack. Offering the software as a service or using it in a commercial cloud environment without express permission is strictly prohibited.
|
||||
*/
|
||||
|
||||
import { PartialType } from '@nestjs/mapped-types';
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { IsBoolean, IsNotEmpty, IsString } from 'class-validator';
|
||||
|
||||
export class LanguageCreateDto {
|
||||
@ApiProperty({ description: 'Language Title', type: String })
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
title: string;
|
||||
|
||||
@ApiProperty({ description: 'Language Code', type: String })
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
code: string;
|
||||
|
||||
@ApiProperty({ description: 'Is Default Language ?', type: Boolean })
|
||||
@IsNotEmpty()
|
||||
@IsBoolean()
|
||||
default: boolean;
|
||||
}
|
||||
|
||||
export class LanguageUpdateDto extends PartialType(LanguageCreateDto) {}
|
@ -7,23 +7,36 @@
|
||||
* 3. SaaS Restriction: This software, or any derivative of it, may not be used to offer a competing product or service (SaaS) without prior written consent from Hexastack. Offering the software as a service or using it in a commercial cloud environment without express permission is strictly prohibited.
|
||||
*/
|
||||
|
||||
import { DynamicModule, Global, Inject, Module } from '@nestjs/common';
|
||||
import {
|
||||
DynamicModule,
|
||||
forwardRef,
|
||||
Global,
|
||||
Inject,
|
||||
Module,
|
||||
} from '@nestjs/common';
|
||||
import { HttpAdapterHost } from '@nestjs/core';
|
||||
import { MongooseModule } from '@nestjs/mongoose';
|
||||
import {
|
||||
I18N_OPTIONS,
|
||||
I18N_TRANSLATIONS,
|
||||
I18nModule as NativeI18nModule,
|
||||
I18nOptions,
|
||||
I18nTranslation,
|
||||
I18nModule as NativeI18nModule,
|
||||
} from 'nestjs-i18n';
|
||||
import { Observable } from 'rxjs';
|
||||
|
||||
import { ChatModule } from '@/chat/chat.module';
|
||||
|
||||
import { LanguageController } from './controllers/language.controller';
|
||||
import { TranslationController } from './controllers/translation.controller';
|
||||
import { LanguageRepository } from './repositories/language.repository';
|
||||
import { TranslationRepository } from './repositories/translation.repository';
|
||||
import { LanguageModel } from './schemas/language.schema';
|
||||
import { TranslationModel } from './schemas/translation.schema';
|
||||
import { LanguageSeeder } from './seeds/language.seed';
|
||||
import { TranslationSeeder } from './seeds/translation.seed';
|
||||
import { I18nService } from './services/i18n.service';
|
||||
import { LanguageService } from './services/language.service';
|
||||
import { TranslationService } from './services/translation.service';
|
||||
|
||||
@Global()
|
||||
@ -43,10 +56,19 @@ export class I18nModule extends NativeI18nModule {
|
||||
const { imports, providers, controllers, exports } = super.forRoot(options);
|
||||
return {
|
||||
module: I18nModule,
|
||||
imports: imports.concat([MongooseModule.forFeature([TranslationModel])]),
|
||||
controllers: controllers.concat([TranslationController]),
|
||||
imports: (imports || []).concat([
|
||||
MongooseModule.forFeature([LanguageModel, TranslationModel]),
|
||||
forwardRef(() => ChatModule),
|
||||
]),
|
||||
controllers: (controllers || []).concat([
|
||||
LanguageController,
|
||||
TranslationController,
|
||||
]),
|
||||
providers: providers.concat([
|
||||
I18nService,
|
||||
LanguageRepository,
|
||||
LanguageService,
|
||||
LanguageSeeder,
|
||||
TranslationRepository,
|
||||
TranslationService,
|
||||
TranslationSeeder,
|
||||
|
23
api/src/i18n/repositories/language.repository.ts
Normal file
23
api/src/i18n/repositories/language.repository.ts
Normal file
@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright © 2024 Hexastack. All rights reserved.
|
||||
*
|
||||
* Licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms:
|
||||
* 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission.
|
||||
* 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file).
|
||||
* 3. SaaS Restriction: This software, or any derivative of it, may not be used to offer a competing product or service (SaaS) without prior written consent from Hexastack. Offering the software as a service or using it in a commercial cloud environment without express permission is strictly prohibited.
|
||||
*/
|
||||
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { InjectModel } from '@nestjs/mongoose';
|
||||
import { Model } from 'mongoose';
|
||||
|
||||
import { BaseRepository } from '@/utils/generics/base-repository';
|
||||
|
||||
import { Language } from '../schemas/language.schema';
|
||||
|
||||
@Injectable()
|
||||
export class LanguageRepository extends BaseRepository<Language> {
|
||||
constructor(@InjectModel(Language.name) readonly model: Model<Language>) {
|
||||
super(model, Language);
|
||||
}
|
||||
}
|
44
api/src/i18n/schemas/language.schema.ts
Normal file
44
api/src/i18n/schemas/language.schema.ts
Normal file
@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright © 2024 Hexastack. All rights reserved.
|
||||
*
|
||||
* Licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms:
|
||||
* 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission.
|
||||
* 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file).
|
||||
* 3. SaaS Restriction: This software, or any derivative of it, may not be used to offer a competing product or service (SaaS) without prior written consent from Hexastack. Offering the software as a service or using it in a commercial cloud environment without express permission is strictly prohibited.
|
||||
*/
|
||||
|
||||
import { Prop, Schema, SchemaFactory, ModelDefinition } from '@nestjs/mongoose';
|
||||
import { THydratedDocument } from 'mongoose';
|
||||
|
||||
import { BaseSchema } from '@/utils/generics/base-schema';
|
||||
|
||||
@Schema({ timestamps: true })
|
||||
export class Language extends BaseSchema {
|
||||
@Prop({
|
||||
type: String,
|
||||
required: true,
|
||||
unique: true,
|
||||
})
|
||||
title: string;
|
||||
|
||||
@Prop({
|
||||
type: String,
|
||||
required: true,
|
||||
unique: true,
|
||||
})
|
||||
code: string;
|
||||
|
||||
@Prop({
|
||||
type: Boolean,
|
||||
})
|
||||
default: boolean;
|
||||
}
|
||||
|
||||
export const LanguageModel: ModelDefinition = {
|
||||
name: Language.name,
|
||||
schema: SchemaFactory.createForClass(Language),
|
||||
};
|
||||
|
||||
export type LanguageDocument = THydratedDocument<Language>;
|
||||
|
||||
export default LanguageModel.schema;
|
23
api/src/i18n/seeds/language.seed-model.ts
Normal file
23
api/src/i18n/seeds/language.seed-model.ts
Normal file
@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright © 2024 Hexastack. All rights reserved.
|
||||
*
|
||||
* Licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms:
|
||||
* 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission.
|
||||
* 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file).
|
||||
* 3. SaaS Restriction: This software, or any derivative of it, may not be used to offer a competing product or service (SaaS) without prior written consent from Hexastack. Offering the software as a service or using it in a commercial cloud environment without express permission is strictly prohibited.
|
||||
*/
|
||||
|
||||
import { LanguageCreateDto } from '../dto/language.dto';
|
||||
|
||||
export const languageModels: LanguageCreateDto[] = [
|
||||
{
|
||||
title: 'English',
|
||||
code: 'en',
|
||||
default: true,
|
||||
},
|
||||
{
|
||||
title: 'Français',
|
||||
code: 'fr',
|
||||
default: false,
|
||||
},
|
||||
];
|
22
api/src/i18n/seeds/language.seed.ts
Normal file
22
api/src/i18n/seeds/language.seed.ts
Normal file
@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright © 2024 Hexastack. All rights reserved.
|
||||
*
|
||||
* Licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms:
|
||||
* 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission.
|
||||
* 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file).
|
||||
* 3. SaaS Restriction: This software, or any derivative of it, may not be used to offer a competing product or service (SaaS) without prior written consent from Hexastack. Offering the software as a service or using it in a commercial cloud environment without express permission is strictly prohibited.
|
||||
*/
|
||||
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { BaseSeeder } from '@/utils/generics/base-seeder';
|
||||
|
||||
import { LanguageRepository } from '../repositories/language.repository';
|
||||
import { Language } from '../schemas/language.schema';
|
||||
|
||||
@Injectable()
|
||||
export class LanguageSeeder extends BaseSeeder<Language> {
|
||||
constructor(private readonly languageRepository: LanguageRepository) {
|
||||
super(languageRepository);
|
||||
}
|
||||
}
|
22
api/src/i18n/services/language.service.ts
Normal file
22
api/src/i18n/services/language.service.ts
Normal file
@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright © 2024 Hexastack. All rights reserved.
|
||||
*
|
||||
* Licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms:
|
||||
* 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission.
|
||||
* 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file).
|
||||
* 3. SaaS Restriction: This software, or any derivative of it, may not be used to offer a competing product or service (SaaS) without prior written consent from Hexastack. Offering the software as a service or using it in a commercial cloud environment without express permission is strictly prohibited.
|
||||
*/
|
||||
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { BaseService } from '@/utils/generics/base-service';
|
||||
|
||||
import { LanguageRepository } from '../repositories/language.repository';
|
||||
import { Language } from '../schemas/language.schema';
|
||||
|
||||
@Injectable()
|
||||
export class LanguageService extends BaseService<Language> {
|
||||
constructor(readonly repository: LanguageRepository) {
|
||||
super(repository);
|
||||
}
|
||||
}
|
@ -13,6 +13,8 @@ import { CategorySeeder } from './chat/seeds/category.seed';
|
||||
import { categoryModels } from './chat/seeds/category.seed-model';
|
||||
import { ContextVarSeeder } from './chat/seeds/context-var.seed';
|
||||
import { contextVarModels } from './chat/seeds/context-var.seed-model';
|
||||
import { LanguageSeeder } from './i18n/seeds/language.seed';
|
||||
import { languageModels } from './i18n/seeds/language.seed-model';
|
||||
import { TranslationSeeder } from './i18n/seeds/translation.seed';
|
||||
import { translationModels } from './i18n/seeds/translation.seed-model';
|
||||
import { LoggerService } from './logger/logger.service';
|
||||
@ -40,6 +42,7 @@ export async function seedDatabase(app: INestApplicationContext) {
|
||||
const settingSeeder = app.get(SettingSeeder);
|
||||
const permissionSeeder = app.get(PermissionSeeder);
|
||||
const userSeeder = app.get(UserSeeder);
|
||||
const languageSeeder = app.get(LanguageSeeder);
|
||||
const translationSeeder = app.get(TranslationSeeder);
|
||||
const nlpEntitySeeder = app.get(NlpEntitySeeder);
|
||||
const nlpValueSeeder = app.get(NlpValueSeeder);
|
||||
@ -127,6 +130,14 @@ export async function seedDatabase(app: INestApplicationContext) {
|
||||
throw e;
|
||||
}
|
||||
|
||||
// Seed languages
|
||||
try {
|
||||
await languageSeeder.seed(languageModels);
|
||||
} catch (e) {
|
||||
logger.error('Unable to seed the database with languages!');
|
||||
throw e;
|
||||
}
|
||||
|
||||
// Seed translations
|
||||
try {
|
||||
await translationSeeder.seed(translationModels);
|
||||
|
@ -100,6 +100,11 @@ export const modelModels: ModelCreateDto[] = [
|
||||
identity: 'subscriber',
|
||||
attributes: {},
|
||||
},
|
||||
{
|
||||
name: 'Language',
|
||||
identity: 'language',
|
||||
attributes: {},
|
||||
},
|
||||
{
|
||||
name: 'Translation',
|
||||
identity: 'translation',
|
||||
|
@ -26,6 +26,7 @@ export type TModel =
|
||||
| 'conversation'
|
||||
| 'message'
|
||||
| 'subscriber'
|
||||
| 'language'
|
||||
| 'translation'
|
||||
| 'botstats'
|
||||
| 'menu'
|
||||
|
@ -7,6 +7,7 @@
|
||||
* 3. SaaS Restriction: This software, or any derivative of it, may not be used to offer a competing product or service (SaaS) without prior written consent from Hexastack. Offering the software as a service or using it in a commercial cloud environment without express permission is strictly prohibited.
|
||||
*/
|
||||
|
||||
import { CheckCircle } from "@mui/icons-material";
|
||||
import AdminPanelSettingsIcon from "@mui/icons-material/AdminPanelSettingsOutlined";
|
||||
import DeleteIcon from "@mui/icons-material/DeleteOutlined";
|
||||
import EditIcon from "@mui/icons-material/EditOutlined";
|
||||
@ -39,12 +40,16 @@ export enum ActionColumnLabel {
|
||||
Content = "button.content",
|
||||
Fields = "button.fields",
|
||||
Manage_Labels = "title.manage_labels",
|
||||
Toggle = "button.toggle",
|
||||
}
|
||||
|
||||
export interface ActionColumn<T extends GridValidRowModel> {
|
||||
label: ActionColumnLabel;
|
||||
action?: (row: T) => void;
|
||||
requires?: PermissionAction[];
|
||||
getState?: (row: T) => boolean;
|
||||
helperText?: string;
|
||||
isDisabled?: (row: T) => boolean;
|
||||
}
|
||||
|
||||
const BUTTON_WIDTH = 60;
|
||||
@ -70,6 +75,8 @@ function getIcon(label: ActionColumnLabel) {
|
||||
return <TocOutlinedIcon />;
|
||||
case ActionColumnLabel.Manage_Labels:
|
||||
return <LocalOfferIcon />;
|
||||
case ActionColumnLabel.Toggle:
|
||||
return <CheckCircle />;
|
||||
default:
|
||||
return <></>;
|
||||
}
|
||||
@ -78,7 +85,7 @@ function getIcon(label: ActionColumnLabel) {
|
||||
function getColor(label: ActionColumnLabel) {
|
||||
switch (label) {
|
||||
case ActionColumnLabel.Edit:
|
||||
return theme.palette.warning.main;
|
||||
return theme.palette.grey[900];
|
||||
case ActionColumnLabel.Delete:
|
||||
return theme.palette.error.main;
|
||||
default:
|
||||
@ -97,29 +104,46 @@ function StackComponent<T extends GridValidRowModel>({
|
||||
|
||||
return (
|
||||
<Stack height="100%" alignItems="center" direction="row" spacing={0.5}>
|
||||
{actions.map(({ label, action, requires = [] }) => (
|
||||
<GridActionsCellItem
|
||||
key={label}
|
||||
className="actionButton"
|
||||
icon={<Tooltip title={t(label)}>{getIcon(label)}</Tooltip>}
|
||||
label={t(label)}
|
||||
showInMenu={false}
|
||||
sx={{
|
||||
color: "grey",
|
||||
"&:hover": {
|
||||
color: getColor(label),
|
||||
},
|
||||
}}
|
||||
disabled={
|
||||
params.row.builtin &&
|
||||
(requires.includes(PermissionAction.UPDATE) ||
|
||||
requires.includes(PermissionAction.DELETE))
|
||||
}
|
||||
onClick={() => {
|
||||
action && action(params.row);
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
{actions.map(
|
||||
({
|
||||
label,
|
||||
action,
|
||||
requires = [],
|
||||
getState,
|
||||
helperText,
|
||||
isDisabled,
|
||||
}) => (
|
||||
<GridActionsCellItem
|
||||
key={label}
|
||||
className="actionButton"
|
||||
icon={
|
||||
<Tooltip title={helperText || t(label)}>{getIcon(label)}</Tooltip>
|
||||
}
|
||||
label={helperText || t(label)}
|
||||
showInMenu={false}
|
||||
sx={{
|
||||
color:
|
||||
label === ActionColumnLabel.Toggle &&
|
||||
getState &&
|
||||
getState(params.row)
|
||||
? getColor(label)
|
||||
: theme.palette.grey[600],
|
||||
"&:hover": {
|
||||
color: getColor(label),
|
||||
},
|
||||
}}
|
||||
disabled={
|
||||
(isDisabled && isDisabled(params.row)) ||
|
||||
(params.row.builtin &&
|
||||
(requires.includes(PermissionAction.UPDATE) ||
|
||||
requires.includes(PermissionAction.DELETE)))
|
||||
}
|
||||
onClick={() => {
|
||||
action && action(params.row);
|
||||
}}
|
||||
/>
|
||||
),
|
||||
)}
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
130
frontend/src/components/languages/LanguageDialog.tsx
Normal file
130
frontend/src/components/languages/LanguageDialog.tsx
Normal file
@ -0,0 +1,130 @@
|
||||
/*
|
||||
* Copyright © 2024 Hexastack. All rights reserved.
|
||||
*
|
||||
* Licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms:
|
||||
* 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission.
|
||||
* 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file).
|
||||
* 3. SaaS Restriction: This software, or any derivative of it, may not be used to offer a competing product or service (SaaS) without prior written consent from Hexastack. Offering the software as a service or using it in a commercial cloud environment without express permission is strictly prohibited.
|
||||
*/
|
||||
|
||||
import { Dialog, DialogActions, DialogContent } from "@mui/material";
|
||||
import { FC, useEffect } from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import DialogButtons from "@/app-components/buttons/DialogButtons";
|
||||
import { DialogTitle } from "@/app-components/dialogs/DialogTitle";
|
||||
import { ContentContainer } from "@/app-components/dialogs/layouts/ContentContainer";
|
||||
import { ContentItem } from "@/app-components/dialogs/layouts/ContentItem";
|
||||
import { Input } from "@/app-components/inputs/Input";
|
||||
import { useCreate } from "@/hooks/crud/useCreate";
|
||||
import { useUpdate } from "@/hooks/crud/useUpdate";
|
||||
import { DialogControlProps } from "@/hooks/useDialog";
|
||||
import { useToast } from "@/hooks/useToast";
|
||||
import { EntityType } from "@/services/types";
|
||||
import { ILanguage, ILanguageAttributes } from "@/types/language.types";
|
||||
|
||||
export type LanguageDialogProps = DialogControlProps<ILanguage>;
|
||||
export const LanguageDialog: FC<LanguageDialogProps> = ({
|
||||
open,
|
||||
data,
|
||||
closeDialog,
|
||||
...rest
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const { toast } = useToast();
|
||||
const { mutateAsync: createLanguage } = useCreate(EntityType.LANGUAGE, {
|
||||
onError: () => {
|
||||
toast.error(t("message.internal_server_error"));
|
||||
},
|
||||
onSuccess() {
|
||||
closeDialog();
|
||||
toast.success(t("message.success_save"));
|
||||
},
|
||||
});
|
||||
const { mutateAsync: updateLanguage } = useUpdate(EntityType.LANGUAGE, {
|
||||
onError: () => {
|
||||
toast.error(t("message.internal_server_error"));
|
||||
},
|
||||
onSuccess() {
|
||||
closeDialog();
|
||||
toast.success(t("message.success_save"));
|
||||
},
|
||||
});
|
||||
const {
|
||||
reset,
|
||||
register,
|
||||
formState: { errors },
|
||||
handleSubmit,
|
||||
} = useForm<ILanguageAttributes>({
|
||||
defaultValues: {
|
||||
title: data?.title || "",
|
||||
code: data?.code || "",
|
||||
},
|
||||
});
|
||||
const validationRules = {
|
||||
title: {
|
||||
required: t("message.title_is_required"),
|
||||
},
|
||||
code: {
|
||||
required: t("message.code_is_required"),
|
||||
},
|
||||
};
|
||||
const onSubmitForm = async (params: ILanguageAttributes) => {
|
||||
if (data) {
|
||||
updateLanguage({ id: data.id, params });
|
||||
} else {
|
||||
createLanguage(params);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (open) reset();
|
||||
}, [open, reset]);
|
||||
|
||||
useEffect(() => {
|
||||
if (data) {
|
||||
reset({
|
||||
title: data.title,
|
||||
code: data.code,
|
||||
});
|
||||
} else {
|
||||
reset();
|
||||
}
|
||||
}, [data, reset]);
|
||||
|
||||
return (
|
||||
<Dialog open={open} fullWidth onClose={closeDialog} {...rest}>
|
||||
<form onSubmit={handleSubmit(onSubmitForm)}>
|
||||
<DialogTitle onClose={closeDialog}>
|
||||
{data ? t("title.edit_label") : t("title.new_label")}
|
||||
</DialogTitle>
|
||||
<DialogContent>
|
||||
<ContentContainer>
|
||||
<ContentItem>
|
||||
<Input
|
||||
label={t("label.title")}
|
||||
error={!!errors.title}
|
||||
{...register("title", validationRules.title)}
|
||||
helperText={errors.title ? errors.title.message : null}
|
||||
multiline={true}
|
||||
/>
|
||||
</ContentItem>
|
||||
<ContentItem>
|
||||
<Input
|
||||
label={t("label.code")}
|
||||
error={!!errors.code}
|
||||
{...register("code", validationRules.code)}
|
||||
helperText={errors.code ? errors.code.message : null}
|
||||
multiline={true}
|
||||
/>
|
||||
</ContentItem>
|
||||
</ContentContainer>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<DialogButtons closeDialog={closeDialog} />
|
||||
</DialogActions>
|
||||
</form>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
204
frontend/src/components/languages/index.tsx
Normal file
204
frontend/src/components/languages/index.tsx
Normal file
@ -0,0 +1,204 @@
|
||||
/*
|
||||
* Copyright © 2024 Hexastack. All rights reserved.
|
||||
*
|
||||
* Licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms:
|
||||
* 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission.
|
||||
* 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file).
|
||||
* 3. SaaS Restriction: This software, or any derivative of it, may not be used to offer a competing product or service (SaaS) without prior written consent from Hexastack. Offering the software as a service or using it in a commercial cloud environment without express permission is strictly prohibited.
|
||||
*/
|
||||
|
||||
import { Flag } from "@mui/icons-material";
|
||||
import AddIcon from "@mui/icons-material/Add";
|
||||
import { Button, Grid, Paper } from "@mui/material";
|
||||
import { GridColDef } from "@mui/x-data-grid";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import { DeleteDialog } from "@/app-components/dialogs/DeleteDialog";
|
||||
import { FilterTextfield } from "@/app-components/inputs/FilterTextfield";
|
||||
import {
|
||||
ActionColumnLabel,
|
||||
useActionColumns,
|
||||
} from "@/app-components/tables/columns/getColumns";
|
||||
import { renderHeader } from "@/app-components/tables/columns/renderHeader";
|
||||
import { DataGrid } from "@/app-components/tables/DataGrid";
|
||||
import { useDelete } from "@/hooks/crud/useDelete";
|
||||
import { useFind } from "@/hooks/crud/useFind";
|
||||
import { useUpdate } from "@/hooks/crud/useUpdate";
|
||||
import { getDisplayDialogs, useDialog } from "@/hooks/useDialog";
|
||||
import { useHasPermission } from "@/hooks/useHasPermission";
|
||||
import { useSearch } from "@/hooks/useSearch";
|
||||
import { useToast } from "@/hooks/useToast";
|
||||
import { PageHeader } from "@/layout/content/PageHeader";
|
||||
import { EntityType } from "@/services/types";
|
||||
import { ILanguage } from "@/types/language.types";
|
||||
import { PermissionAction } from "@/types/permission.types";
|
||||
import { getDateTimeFormatter } from "@/utils/date";
|
||||
|
||||
import { LanguageDialog } from "./LanguageDialog";
|
||||
|
||||
export const Languages = () => {
|
||||
const { t } = useTranslation();
|
||||
const { toast } = useToast();
|
||||
const addDialogCtl = useDialog<ILanguage>(false);
|
||||
const editDialogCtl = useDialog<ILanguage>(false);
|
||||
const deleteDialogCtl = useDialog<string>(false);
|
||||
const hasPermission = useHasPermission();
|
||||
const { onSearch, searchPayload } = useSearch<ILanguage>({
|
||||
$or: ["title", "code"],
|
||||
});
|
||||
const { dataGridProps, refetch } = useFind(
|
||||
{ entity: EntityType.LANGUAGE },
|
||||
{
|
||||
params: searchPayload,
|
||||
},
|
||||
);
|
||||
const { mutateAsync: updateLanguage } = useUpdate(EntityType.LANGUAGE, {
|
||||
onError: () => {
|
||||
toast.error(t("message.internal_server_error"));
|
||||
},
|
||||
onSuccess() {
|
||||
refetch();
|
||||
toast.success(t("message.success_save"));
|
||||
},
|
||||
});
|
||||
const { mutateAsync: deleteLanguage } = useDelete(EntityType.LANGUAGE, {
|
||||
onError: () => {
|
||||
toast.error(t("message.internal_server_error"));
|
||||
},
|
||||
onSuccess() {
|
||||
deleteDialogCtl.closeDialog();
|
||||
toast.success(t("message.item_delete_success"));
|
||||
},
|
||||
});
|
||||
const toggleDefault = (row: ILanguage) => {
|
||||
if (!row.default) {
|
||||
updateLanguage({
|
||||
id: row.id,
|
||||
params: {
|
||||
default: true,
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
||||
const actionColumns = useActionColumns<ILanguage>(
|
||||
EntityType.LANGUAGE,
|
||||
[
|
||||
{
|
||||
label: ActionColumnLabel.Toggle,
|
||||
action: (row) => toggleDefault(row),
|
||||
requires: [PermissionAction.UPDATE],
|
||||
getState: (row) => row.default,
|
||||
helperText: t("button.mark_as_default"),
|
||||
},
|
||||
{
|
||||
label: ActionColumnLabel.Edit,
|
||||
action: (row) => editDialogCtl.openDialog(row),
|
||||
requires: [PermissionAction.UPDATE],
|
||||
},
|
||||
{
|
||||
label: ActionColumnLabel.Delete,
|
||||
action: (row) => deleteDialogCtl.openDialog(row.id),
|
||||
requires: [PermissionAction.DELETE],
|
||||
isDisabled: (row) => row.default,
|
||||
},
|
||||
],
|
||||
t("label.operations"),
|
||||
);
|
||||
const columns: GridColDef<ILanguage>[] = [
|
||||
{ field: "id", headerName: "ID" },
|
||||
{
|
||||
flex: 2,
|
||||
field: "title",
|
||||
headerName: t("label.title"),
|
||||
disableColumnMenu: true,
|
||||
renderHeader,
|
||||
headerAlign: "left",
|
||||
},
|
||||
{
|
||||
flex: 1,
|
||||
field: "code",
|
||||
headerName: t("label.code"),
|
||||
disableColumnMenu: true,
|
||||
renderHeader,
|
||||
headerAlign: "left",
|
||||
},
|
||||
{
|
||||
flex: 1,
|
||||
field: "default",
|
||||
headerName: t("label.default"),
|
||||
disableColumnMenu: true,
|
||||
renderHeader,
|
||||
headerAlign: "left",
|
||||
valueGetter: (value) => (value ? t("label.yes") : t("label.no")),
|
||||
},
|
||||
{
|
||||
minWidth: 140,
|
||||
field: "createdAt",
|
||||
headerName: t("label.createdAt"),
|
||||
disableColumnMenu: true,
|
||||
renderHeader,
|
||||
resizable: false,
|
||||
headerAlign: "left",
|
||||
valueGetter: (params) =>
|
||||
t("datetime.created_at", getDateTimeFormatter(params)),
|
||||
},
|
||||
{
|
||||
minWidth: 140,
|
||||
field: "updatedAt",
|
||||
headerName: t("label.updatedAt"),
|
||||
disableColumnMenu: true,
|
||||
renderHeader,
|
||||
resizable: false,
|
||||
headerAlign: "left",
|
||||
valueGetter: (params) =>
|
||||
t("datetime.updated_at", getDateTimeFormatter(params)),
|
||||
},
|
||||
actionColumns,
|
||||
];
|
||||
|
||||
return (
|
||||
<Grid container gap={3} flexDirection="column">
|
||||
<LanguageDialog {...getDisplayDialogs(addDialogCtl)} />
|
||||
<LanguageDialog {...getDisplayDialogs(editDialogCtl)} />
|
||||
<DeleteDialog
|
||||
{...deleteDialogCtl}
|
||||
callback={() => {
|
||||
if (deleteDialogCtl?.data) deleteLanguage(deleteDialogCtl.data);
|
||||
}}
|
||||
/>
|
||||
<PageHeader icon={Flag} title={t("title.languages")}>
|
||||
<Grid
|
||||
justifyContent="flex-end"
|
||||
gap={1}
|
||||
container
|
||||
alignItems="center"
|
||||
flexShrink={0}
|
||||
width="max-content"
|
||||
>
|
||||
<Grid item>
|
||||
<FilterTextfield onChange={onSearch} />
|
||||
</Grid>
|
||||
{hasPermission(EntityType.LANGUAGE, PermissionAction.CREATE) ? (
|
||||
<Grid item>
|
||||
<Button
|
||||
startIcon={<AddIcon />}
|
||||
variant="contained"
|
||||
sx={{ float: "right" }}
|
||||
onClick={() => addDialogCtl.openDialog()}
|
||||
>
|
||||
{t("button.add")}
|
||||
</Button>
|
||||
</Grid>
|
||||
) : null}
|
||||
</Grid>
|
||||
</PageHeader>
|
||||
<Grid item xs={12}>
|
||||
<Paper sx={{ padding: 2 }}>
|
||||
<Grid>
|
||||
<DataGrid columns={columns} {...dataGridProps} />
|
||||
</Grid>
|
||||
</Paper>
|
||||
</Grid>
|
||||
</Grid>
|
||||
);
|
||||
};
|
@ -122,6 +122,8 @@
|
||||
"cms": "CMS",
|
||||
"nodes": "Content",
|
||||
"entities": "Content types",
|
||||
"languages": "Languages",
|
||||
"manage_localization": "Manage Localization",
|
||||
"translations": "Translations",
|
||||
"import": "Bulk Import",
|
||||
"media_library": "Media Library",
|
||||
@ -183,6 +185,7 @@
|
||||
"edit_node": "Edit Content",
|
||||
"import": "Bulk Import",
|
||||
"media_library": "Media Library",
|
||||
"languages": "Languages",
|
||||
"translations": "Translations",
|
||||
"update_translation": "Update Translation",
|
||||
"broadcast": "Broadcast",
|
||||
@ -544,7 +547,9 @@
|
||||
"total": "Total",
|
||||
"general": "General",
|
||||
"other": "Other",
|
||||
"no_data": "No data"
|
||||
"no_data": "No data",
|
||||
"code": "Code",
|
||||
"default": "Default"
|
||||
},
|
||||
"placeholder": {
|
||||
"your_username": "Your username",
|
||||
@ -642,7 +647,8 @@
|
||||
"media_library": "Media Library",
|
||||
"manage_roles": "Manage Roles",
|
||||
"connect_with_sso": "Connect with SSO",
|
||||
"add_pattern": "Add pattern"
|
||||
"add_pattern": "Add pattern",
|
||||
"mark_as_default": "Mark as Default"
|
||||
},
|
||||
"input": {
|
||||
"search": "Search"
|
||||
|
@ -123,6 +123,8 @@
|
||||
"cms": "CMS",
|
||||
"nodes": "Contenu",
|
||||
"entities": "Types de contenu",
|
||||
"manage_localization": "Internationalisation",
|
||||
"languages": "Langues",
|
||||
"translations": "Traductions",
|
||||
"import": "Importation en masse",
|
||||
"media_library": "Bibliothéque Media",
|
||||
@ -184,6 +186,7 @@
|
||||
"edit_node": "Modifier le contenu",
|
||||
"import": "Importation en masse",
|
||||
"media_library": "Bibliothéque Media",
|
||||
"languages": "Langues",
|
||||
"translations": "Traductions",
|
||||
"update_translation": "Mettre à jour la traduction",
|
||||
"broadcast": "Diffusion",
|
||||
@ -544,7 +547,9 @@
|
||||
"total": "Totale",
|
||||
"general": "Général",
|
||||
"other": "Autre",
|
||||
"no_data": "Pas de données"
|
||||
"no_data": "Pas de données",
|
||||
"code": "Code",
|
||||
"default": "Par Défaut"
|
||||
},
|
||||
"placeholder": {
|
||||
"your_username": "Votre nom d'utilisateur",
|
||||
@ -578,7 +583,8 @@
|
||||
"start_date": "Date de début",
|
||||
"end_date": "Date de fin",
|
||||
"nlp_value": "Valeur",
|
||||
"type_message_here": "Ecrivez quelque chose ici ...."
|
||||
"type_message_here": "Ecrivez quelque chose ici ....",
|
||||
"mark_as_default": "Par Défaut"
|
||||
},
|
||||
"button": {
|
||||
"login": "Se connecter",
|
||||
|
@ -21,6 +21,7 @@ import {
|
||||
faUsers,
|
||||
IconDefinition,
|
||||
} from "@fortawesome/free-solid-svg-icons";
|
||||
import { Flag, Language } from "@mui/icons-material";
|
||||
import AppsIcon from "@mui/icons-material/Apps";
|
||||
import ChevronLeftIcon from "@mui/icons-material/ChevronLeft";
|
||||
import DriveFolderUploadIcon from "@mui/icons-material/DriveFolderUpload";
|
||||
@ -175,14 +176,6 @@ const getMenuItems = (ssoEnabled: boolean): MenuItem[] => [
|
||||
[EntityType.CONTENT_TYPE]: [PermissionAction.READ],
|
||||
},
|
||||
},
|
||||
{
|
||||
text: "menu.translations",
|
||||
href: "/translations",
|
||||
Icon: faLanguage,
|
||||
requires: {
|
||||
[EntityType.TRANSLATION]: [PermissionAction.READ],
|
||||
},
|
||||
},
|
||||
{
|
||||
text: "menu.media_library",
|
||||
href: "/content/media-library",
|
||||
@ -249,6 +242,28 @@ const getMenuItems = (ssoEnabled: boolean): MenuItem[] => [
|
||||
: []),
|
||||
],
|
||||
},
|
||||
{
|
||||
text: "menu.manage_localization",
|
||||
Icon: Language,
|
||||
submenuItems: [
|
||||
{
|
||||
text: "menu.languages",
|
||||
href: "/localization/languages",
|
||||
Icon: Flag,
|
||||
requires: {
|
||||
[EntityType.LANGUAGE]: [PermissionAction.READ],
|
||||
},
|
||||
},
|
||||
{
|
||||
text: "menu.translations",
|
||||
href: "/localization/translations",
|
||||
Icon: faLanguage,
|
||||
requires: {
|
||||
[EntityType.TRANSLATION]: [PermissionAction.READ],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: "menu.settings",
|
||||
href: "/settings",
|
||||
|
23
frontend/src/pages/localization/languages.tsx
Normal file
23
frontend/src/pages/localization/languages.tsx
Normal file
@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright © 2024 Hexastack. All rights reserved.
|
||||
*
|
||||
* Licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms:
|
||||
* 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission.
|
||||
* 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file).
|
||||
* 3. SaaS Restriction: This software, or any derivative of it, may not be used to offer a competing product or service (SaaS) without prior written consent from Hexastack. Offering the software as a service or using it in a commercial cloud environment without express permission is strictly prohibited.
|
||||
*/
|
||||
|
||||
import { ReactElement } from "react";
|
||||
|
||||
import { Languages } from "@/components/languages";
|
||||
import { Layout } from "@/layout";
|
||||
|
||||
const LanguagesPage = () => {
|
||||
return <Languages />;
|
||||
};
|
||||
|
||||
LanguagesPage.getLayout = function getLayout(page: ReactElement) {
|
||||
return <Layout>{page}</Layout>;
|
||||
};
|
||||
|
||||
export default LanguagesPage;
|
@ -59,6 +59,7 @@ export const ROUTES = {
|
||||
[EntityType.NLP_VALUE]: "/nlpvalue",
|
||||
[EntityType.NLP_SAMPLE_ENTITY]: "",
|
||||
[EntityType.MESSAGE]: "/message",
|
||||
[EntityType.LANGUAGE]: "/language",
|
||||
[EntityType.TRANSLATION]: "/translation",
|
||||
[EntityType.ATTACHMENT]: "/attachment",
|
||||
[EntityType.CHANNEL]: "/channel",
|
||||
|
@ -210,6 +210,15 @@ export const NlpSampleEntityEntity = new schema.Entity(
|
||||
},
|
||||
);
|
||||
|
||||
export const LanguageEntity = new schema.Entity(
|
||||
EntityType.LANGUAGE,
|
||||
undefined,
|
||||
{
|
||||
idAttribute: ({ id }) => id,
|
||||
processStrategy: processCommonStrategy,
|
||||
},
|
||||
);
|
||||
|
||||
export const TranslationEntity = new schema.Entity(
|
||||
EntityType.TRANSLATION,
|
||||
undefined,
|
||||
@ -280,6 +289,7 @@ export const ENTITY_MAP = {
|
||||
[EntityType.NLP_ENTITY]: NlpEntityEntity,
|
||||
[EntityType.NLP_SAMPLE_ENTITY]: NlpSampleEntityEntity,
|
||||
[EntityType.NLP_VALUE]: NlpValueEntity,
|
||||
[EntityType.LANGUAGE]: LanguageEntity,
|
||||
[EntityType.TRANSLATION]: TranslationEntity,
|
||||
[EntityType.ATTACHMENT]: AttachmentEntity,
|
||||
[EntityType.BLOCK]: BlockEntity,
|
||||
|
@ -32,6 +32,7 @@ export enum EntityType {
|
||||
NLP_VALUE = "NlpValue",
|
||||
MESSAGE = "Message",
|
||||
MENU = "Menu",
|
||||
LANGUAGE = "Language",
|
||||
TRANSLATION = "Translation",
|
||||
ATTACHMENT = "Attachment",
|
||||
CHANNEL = "Channel",
|
||||
|
@ -24,6 +24,7 @@ import { IContentType, IContentTypeAttributes } from "./content-type.types";
|
||||
import { IContent, IContentAttributes, IContentFull } from "./content.types";
|
||||
import { IContextVar, IContextVarAttributes } from "./context-var.types";
|
||||
import { ILabel, ILabelAttributes, ILabelFull } from "./label.types";
|
||||
import { ILanguage, ILanguageAttributes } from "./language.types";
|
||||
import {
|
||||
IMenuNode,
|
||||
IMenuNodeAttributes,
|
||||
@ -106,6 +107,7 @@ export const POPULATE_BY_TYPE = {
|
||||
[EntityType.MESSAGE]: ["sender", "recipient", "sentBy"],
|
||||
[EntityType.MENU]: ["parent"],
|
||||
[EntityType.MENUTREE]: [],
|
||||
[EntityType.LANGUAGE]: [],
|
||||
[EntityType.TRANSLATION]: [],
|
||||
[EntityType.ATTACHMENT]: [],
|
||||
[EntityType.CUSTOM_BLOCK]: [],
|
||||
@ -189,6 +191,7 @@ export interface IEntityMapTypes {
|
||||
ISubscriber,
|
||||
ISubscriberFull
|
||||
>;
|
||||
[EntityType.LANGUAGE]: IEntityTypes<ILanguageAttributes, ILanguage>;
|
||||
[EntityType.TRANSLATION]: IEntityTypes<ITranslationAttributes, ITranslation>;
|
||||
[EntityType.USER]: IEntityTypes<IUserAttributes, IUser, IUserFull>;
|
||||
[EntityType.ATTACHMENT]: IEntityTypes<IAttachmentAttributes, IAttachment>;
|
||||
|
26
frontend/src/types/language.types.ts
Normal file
26
frontend/src/types/language.types.ts
Normal file
@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright © 2024 Hexastack. All rights reserved.
|
||||
*
|
||||
* Licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms:
|
||||
* 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission.
|
||||
* 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file).
|
||||
* 3. SaaS Restriction: This software, or any derivative of it, may not be used to offer a competing product or service (SaaS) without prior written consent from Hexastack. Offering the software as a service or using it in a commercial cloud environment without express permission is strictly prohibited.
|
||||
*/
|
||||
|
||||
import { EntityType, Format } from "@/services/types";
|
||||
|
||||
import { IBaseSchema, IFormat, OmitPopulate } from "./base.types";
|
||||
|
||||
export type ILanguages = Record<string, string>;
|
||||
|
||||
export interface ILanguageAttributes {
|
||||
title: string;
|
||||
code: string;
|
||||
default: boolean;
|
||||
}
|
||||
|
||||
export interface ILanguageStub
|
||||
extends IBaseSchema,
|
||||
OmitPopulate<ILanguageAttributes, EntityType.TRANSLATION> {}
|
||||
|
||||
export interface ILanguage extends ILanguageStub, IFormat<Format.BASIC> {}
|
@ -7,9 +7,9 @@
|
||||
* 3. SaaS Restriction: This software, or any derivative of it, may not be used to offer a competing product or service (SaaS) without prior written consent from Hexastack. Offering the software as a service or using it in a commercial cloud environment without express permission is strictly prohibited.
|
||||
*/
|
||||
|
||||
import { Format } from "@/services/types";
|
||||
import { EntityType, Format } from "@/services/types";
|
||||
|
||||
import { IBaseSchema, IFormat } from "./base.types";
|
||||
import { IBaseSchema, IFormat, OmitPopulate } from "./base.types";
|
||||
|
||||
export type ITranslations = Record<string, string>;
|
||||
|
||||
@ -19,11 +19,8 @@ export interface ITranslationAttributes {
|
||||
translated: number;
|
||||
}
|
||||
|
||||
export interface ITranslationStub extends IBaseSchema {
|
||||
str: string;
|
||||
translations: ITranslations;
|
||||
translated: number;
|
||||
}
|
||||
export interface ITranslationStub
|
||||
extends IBaseSchema,
|
||||
OmitPopulate<ITranslationAttributes, EntityType.TRANSLATION> {}
|
||||
|
||||
export interface ITranslation extends ITranslationStub, IFormat<Format.BASIC> {}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user