diff --git a/apps/nestjs-backend/package.json b/apps/nestjs-backend/package.json index 2573e405ad..51d419ea55 100644 --- a/apps/nestjs-backend/package.json +++ b/apps/nestjs-backend/package.json @@ -118,17 +118,17 @@ "webpack": "5.91.0" }, "dependencies": { - "@ai-sdk/amazon-bedrock": "4.0.0-beta.97", - "@ai-sdk/anthropic": "3.0.0-beta.87", - "@ai-sdk/azure": "3.0.0-beta.102", - "@ai-sdk/cohere": "3.0.0-beta.52", - "@ai-sdk/deepseek": "2.0.0-beta.54", - "@ai-sdk/google": "3.0.0-beta.77", - "@ai-sdk/mistral": "3.0.0-beta.53", - "@ai-sdk/openai": "3.0.0-beta.100", - "@ai-sdk/openai-compatible": "2.0.0-beta.52", - "@ai-sdk/togetherai": "2.0.0-beta.53", - "@ai-sdk/xai": "3.0.0-beta.60", + "@ai-sdk/amazon-bedrock": "4.0.9", + "@ai-sdk/anthropic": "3.0.7", + "@ai-sdk/azure": "3.0.7", + "@ai-sdk/cohere": "3.0.4", + "@ai-sdk/deepseek": "2.0.4", + "@ai-sdk/google": "3.0.5", + "@ai-sdk/mistral": "3.0.5", + "@ai-sdk/openai": "3.0.7", + "@ai-sdk/openai-compatible": "2.0.4", + "@ai-sdk/togetherai": "2.0.4", + "@ai-sdk/xai": "3.0.10", "@aws-sdk/client-s3": "3.609.0", "@aws-sdk/lib-storage": "3.609.0", "@aws-sdk/s3-request-presigner": "3.609.0", @@ -172,7 +172,7 @@ "@teable/openapi": "workspace:^", "@teamwork/websocket-json-stream": "2.0.0", "@valibot/to-json-schema": "1.3.0", - "ai": "6.0.0-beta.156", + "ai": "6.0.14", "ajv": "8.12.0", "archiver": "7.0.1", "axios": "1.7.7", diff --git a/apps/nestjs-backend/src/features/field/field-calculate/field-converting.service.ts b/apps/nestjs-backend/src/features/field/field-calculate/field-converting.service.ts index 6ba4d0eea3..31f537719d 100644 --- a/apps/nestjs-backend/src/features/field/field-calculate/field-converting.service.ts +++ b/apps/nestjs-backend/src/features/field/field-calculate/field-converting.service.ts @@ -249,6 +249,41 @@ export class FieldConvertingService { return ops.filter(Boolean) as IOtOperation[]; } + /** + * Update conditional lookup field - validate dependencies and clear/set hasError + */ + private updateConditionalLookupField(field: IFieldInstance, fieldMap: IFieldMap): IOtOperation[] { + const ops: IOtOperation[] = []; + + // Get referenced field IDs from the conditional lookup configuration + const referencedFieldIds = this.fieldSupplementService + .getFieldReferenceIds(field) + .filter((id) => !!id && id !== field.id); + + // Check if any referenced field is missing or has error + const missingFields = referencedFieldIds.filter((id) => !fieldMap[id]); + const erroredFields = referencedFieldIds.filter((id) => fieldMap[id]?.hasError); + + const hasMissingDependency = missingFields.length > 0; + const hasErroredDependency = erroredFields.length > 0; + + if (hasMissingDependency || hasErroredDependency) { + const op = this.buildOpAndMutateField(field, 'hasError', true); + if (op) { + ops.push(op); + } + return ops; + } + + // Clear error if all dependencies are valid + const clearErrorOp = this.buildOpAndMutateField(field, 'hasError', null); + if (clearErrorOp) { + ops.push(clearErrorOp); + } + + return ops; + } + private updateConditionalRollupField( field: ConditionalRollupFieldDto, fieldMap: IFieldMap @@ -326,6 +361,7 @@ export class FieldConvertingService { private async generateReferenceFieldOps(fields: IFieldInstance[]) { const fieldIds = fields.map((field) => field.id); + const topoOrdersContext = await this.fieldCalculationService.getTopoOrdersContext(fieldIds); const { fieldId2TableId, directedGraph } = topoOrdersContext; const fieldMap = { ...topoOrdersContext.fieldMap, ...keyBy(fields, 'id') }; @@ -362,7 +398,12 @@ export class FieldConvertingService { const tableId = fieldId2TableId[curField.id]; if (curField.isLookup) { - pushOpsMap(tableId, curField.id, this.updateLookupField(curField, fieldMap)); + // For conditional lookup fields, use the dedicated update method + if (curField.isConditionalLookup) { + pushOpsMap(tableId, curField.id, this.updateConditionalLookupField(curField, fieldMap)); + } else { + pushOpsMap(tableId, curField.id, this.updateLookupField(curField, fieldMap)); + } } else if (curField.type === FieldType.Formula) { pushOpsMap(tableId, curField.id, this.updateFormulaField(curField, fieldMap)); } else if (curField.type === FieldType.Rollup) { @@ -1554,6 +1595,7 @@ export class FieldConvertingService { ); const newField = createFieldInstanceByVo(newFieldVo); + const modifiedOps = await this.generateModifiedOps(tableId, newField, oldField); // 2. collect changes effect by the supplement(link) field diff --git a/apps/nestjs-backend/src/features/field/field-calculate/field-supplement.service.ts b/apps/nestjs-backend/src/features/field/field-calculate/field-supplement.service.ts index 29024f9313..608f91f79c 100644 --- a/apps/nestjs-backend/src/features/field/field-calculate/field-supplement.service.ts +++ b/apps/nestjs-backend/src/features/field/field-calculate/field-supplement.service.ts @@ -1037,6 +1037,8 @@ export class FieldSupplementService { isComputed: true, cellValueType, dbFieldType: this.getDbFieldType(field.type, cellValueType, true), + // Clear hasError since we validated all required fields exist + hasError: undefined, }; } diff --git a/apps/nestjs-backend/src/features/field/open-api/field-open-api.service.ts b/apps/nestjs-backend/src/features/field/open-api/field-open-api.service.ts index be04c4d884..4d5ae0a096 100644 --- a/apps/nestjs-backend/src/features/field/open-api/field-open-api.service.ts +++ b/apps/nestjs-backend/src/features/field/open-api/field-open-api.service.ts @@ -239,7 +239,7 @@ export class FieldOpenApiService { return false; } - return this.validateFilterFieldReferences(tableId, foreignTableId, meta?.filter); + return await this.validateFilterFieldReferences(tableId, foreignTableId, meta?.filter); } private async isFieldConfigurationValid( @@ -552,7 +552,10 @@ export class FieldOpenApiService { return references.every((reference) => { const referenceField = resolveReferenceField(reference); - return referenceField ? isFieldReferenceComparable(baseField, referenceField) : false; + if (!referenceField) { + return false; + } + return isFieldReferenceComparable(baseField, referenceField); }); } @@ -568,20 +571,28 @@ export class FieldOpenApiService { private async markError(tableId: string, field: IFieldInstance, hasError: boolean) { if (hasError) { - !field.hasError && (await this.fieldService.markError(tableId, [field.id], true)); + if (!field.hasError) { + await this.fieldService.markError(tableId, [field.id], true); + } } else { - field.hasError && (await this.fieldService.markError(tableId, [field.id], false)); + if (field.hasError) { + await this.fieldService.markError(tableId, [field.id], false); + } } } private async checkAndUpdateError(tableId: string, field: IFieldInstance) { const fieldReferenceIds = this.fieldSupplementService.getFieldReferenceIds(field); + // Deduplicate field IDs since the same field can appear multiple times + // (e.g., as lookupFieldId and in filter) + const uniqueFieldReferenceIds = [...new Set(fieldReferenceIds)]; + const refFields = await this.prismaService.txClient().field.findMany({ - where: { id: { in: fieldReferenceIds }, deletedTime: null }, + where: { id: { in: uniqueFieldReferenceIds }, deletedTime: null }, select: { id: true }, }); - if (refFields.length !== fieldReferenceIds.length) { + if (refFields.length !== uniqueFieldReferenceIds.length) { await this.markError(tableId, field, true); return; } @@ -591,7 +602,7 @@ export class FieldOpenApiService { toFieldId: field.id, }, }); - const missingReferenceIds = fieldReferenceIds.filter( + const missingReferenceIds = uniqueFieldReferenceIds.filter( (refId) => !curReference.find((ref) => ref.fromFieldId === refId) ); diff --git a/apps/nestjs-backend/src/features/notification/notification.service.ts b/apps/nestjs-backend/src/features/notification/notification.service.ts index b5d582266d..e6488faef2 100644 --- a/apps/nestjs-backend/src/features/notification/notification.service.ts +++ b/apps/nestjs-backend/src/features/notification/notification.service.ts @@ -12,9 +12,8 @@ import { } from '@teable/core'; import type { Prisma } from '@teable/db-main-prisma'; import { PrismaService } from '@teable/db-main-prisma'; +import { MailTransporterType, MailType } from '@teable/openapi'; import { - MailTransporterType, - MailType, type IGetNotifyListQuery, type INotificationUnreadCountVo, type INotificationVo, @@ -48,12 +47,11 @@ export class NotificationService { private readonly i18n: I18nService ) {} - private async getUserLang(userId: string) { - const user = await this.userService.getUserById(userId); - return user?.lang ?? I18nContext.current()?.lang; + getUserLang(lang?: string | null) { + return lang ?? I18nContext.current()?.lang; } - private getMessage(text: string | ILocalization, lang?: string) { + getMessage(text: string | ILocalization, lang?: string) { return typeof text === 'string' ? text : (this.i18n.t(text.i18nKey, { @@ -65,7 +63,7 @@ export class NotificationService { /** * notification message i18n use common prefix, so we need to remove it to save db */ - private getMessageI18n(localization: string | ILocalization) { + getMessageI18n(localization: string | ILocalization) { return typeof localization === 'string' ? undefined : JSON.stringify({ @@ -251,7 +249,7 @@ export class NotificationService { this.sendNotifyBySocket(toUser.id, socketNotification); if (emailConfig && toUser.notifyMeta && toUser.notifyMeta.email) { - const lang = await this.getUserLang(toUserId); + const lang = this.getUserLang(toUser.lang); const emailOptions = await this.mailSenderService.htmlEmailOptions({ ...emailConfig, title: this.getMessage(emailConfig.title, lang), @@ -340,7 +338,7 @@ export class NotificationService { this.sendNotifyBySocket(toUser.id, socketNotification); if (emailConfig && toUser.notifyMeta && toUser.notifyMeta.email) { - const lang = await this.getUserLang(toUserId); + const lang = this.getUserLang(toUser.lang); const emailOptions = await this.mailSenderService.commonEmailOptions({ ...emailConfig, title: this.getMessage(emailConfig.title, lang), diff --git a/apps/nestjs-backend/src/features/record/query-builder/field-cte-visitor.ts b/apps/nestjs-backend/src/features/record/query-builder/field-cte-visitor.ts index 4948f2ec07..811d78e6ce 100644 --- a/apps/nestjs-backend/src/features/record/query-builder/field-cte-visitor.ts +++ b/apps/nestjs-backend/src/features/record/query-builder/field-cte-visitor.ts @@ -425,6 +425,13 @@ class FieldCteSelectionVisitor implements IFieldVisitor { if (field.isConditionalLookup) { const cteName = this.fieldCteMap.get(field.id); if (!cteName) { + // Log warning when conditional lookup CTE is missing + const fieldCteMapKeys = Array.from(this.fieldCteMap.keys()); + console.warn( + `[ConditionalLookup] CTE not found for field ${field.id} (${field.name}). ` + + `Available CTEs: [${fieldCteMapKeys.join(', ')}]. ` + + `Returning NULL::${field.dbFieldType}` + ); return this.dialect.typedNullFor(field.dbFieldType); } return `"${cteName}"."conditional_lookup_${field.id}"`; @@ -1805,7 +1812,12 @@ export class FieldCteVisitor implements IFieldVisitor { field: FieldCore, options: IConditionalLookupOptions ): void { - if (field.hasError) return; + if (field.hasError) { + this.logger.warn( + `[ConditionalLookup] Skipping CTE generation for field ${field.id} (${field.name}): field.hasError=true` + ); + return; + } if (this.state.getFieldCteMap().has(field.id)) return; if (this.conditionalLookupGenerationStack.has(field.id)) return; @@ -1813,16 +1825,28 @@ export class FieldCteVisitor implements IFieldVisitor { try { const { foreignTableId, lookupFieldId, filter, sort, limit } = options; if (!foreignTableId || !lookupFieldId) { + this.logger.warn( + `[ConditionalLookup] Skipping CTE generation for field ${field.id} (${field.name}): ` + + `foreignTableId=${foreignTableId}, lookupFieldId=${lookupFieldId}` + ); return; } const foreignTable = this.tables.getTable(foreignTableId); if (!foreignTable) { + this.logger.warn( + `[ConditionalLookup] Skipping CTE generation for field ${field.id} (${field.name}): ` + + `foreignTable not found for foreignTableId=${foreignTableId}` + ); return; } const targetField = foreignTable.getField(lookupFieldId); if (!targetField) { + this.logger.warn( + `[ConditionalLookup] Skipping CTE generation for field ${field.id} (${field.name}): ` + + `targetField not found for lookupFieldId=${lookupFieldId} in foreignTable=${foreignTableId}` + ); return; } @@ -2135,6 +2159,11 @@ export class FieldCteVisitor implements IFieldVisitor { const options = field.getConditionalLookupOptions?.(); if (options) { this.generateConditionalLookupFieldCte(field, options); + } else { + this.logger.warn( + `[ConditionalLookup] getConditionalLookupOptions returned undefined for field ${field.id} (${field.name}). ` + + `isConditionalLookup=${field.isConditionalLookup}, lookupOptions=${JSON.stringify(field.lookupOptions)}` + ); } } } diff --git a/apps/nestjs-backend/src/features/record/query-builder/field-select-visitor.ts b/apps/nestjs-backend/src/features/record/query-builder/field-select-visitor.ts index f5e173e701..cd222cd5a8 100644 --- a/apps/nestjs-backend/src/features/record/query-builder/field-select-visitor.ts +++ b/apps/nestjs-backend/src/features/record/query-builder/field-select-visitor.ts @@ -207,18 +207,28 @@ export class FieldSelectVisitor implements IFieldVisitor { } // Conditional lookup CTEs are stored against the field itself. - if (field.isConditionalLookup && fieldCteMap.has(field.id)) { - const conditionalCteName = fieldCteMap.get(field.id)!; - if (!this.state.isCteJoined(conditionalCteName)) { - // If the CTE isn't joined in this scope, fall back to raw column access. + if (field.isConditionalLookup) { + if (!fieldCteMap.has(field.id)) { + console.warn( + `[ConditionalLookup] CTE not in fieldCteMap for field ${field.id} (${(field as unknown as { name?: string }).name}). ` + + `Available CTE keys: [${Array.from(fieldCteMap.keys()).join(', ')}]` + ); } else { - const column = - field.type === FieldType.ConditionalRollup - ? `conditional_rollup_${field.id}` - : `conditional_lookup_${field.id}`; - const rawExpression = this.qb.client.raw(`??."${column}"`, [conditionalCteName]); - this.state.setSelection(field.id, `"${conditionalCteName}"."${column}"`); - return rawExpression; + const conditionalCteName = fieldCteMap.get(field.id)!; + if (!this.state.isCteJoined(conditionalCteName)) { + // If the CTE isn't joined in this scope, fall back to raw column access. + console.warn( + `[ConditionalLookup] CTE ${conditionalCteName} for field ${field.id} (${(field as unknown as { name?: string }).name}) is not joined in current scope` + ); + } else { + const column = + field.type === FieldType.ConditionalRollup + ? `conditional_rollup_${field.id}` + : `conditional_lookup_${field.id}`; + const rawExpression = this.qb.client.raw(`??."${column}"`, [conditionalCteName]); + this.state.setSelection(field.id, `"${conditionalCteName}"."${column}"`); + return rawExpression; + } } } diff --git a/apps/nestjs-backend/src/features/setting/open-api/setting-open-api.controller.ts b/apps/nestjs-backend/src/features/setting/open-api/setting-open-api.controller.ts index 0666cd2c83..ae3e60fe01 100644 --- a/apps/nestjs-backend/src/features/setting/open-api/setting-open-api.controller.ts +++ b/apps/nestjs-backend/src/features/setting/open-api/setting-open-api.controller.ts @@ -73,6 +73,7 @@ export class SettingOpenApiController { SettingKey.AI_CONFIG, SettingKey.APP_CONFIG, SettingKey.WEB_SEARCH_CONFIG, + SettingKey.ENABLE_CREDIT_REWARD, ]); const { aiConfig, appConfig, webSearchConfig, ...rest } = setting; return { diff --git a/apps/nestjs-backend/src/types/i18n.generated.ts b/apps/nestjs-backend/src/types/i18n.generated.ts index 0309408c26..3febe6c837 100644 --- a/apps/nestjs-backend/src/types/i18n.generated.ts +++ b/apps/nestjs-backend/src/types/i18n.generated.ts @@ -194,6 +194,7 @@ export type I18nTranslations = { "doNotSave": string; "submit": string; "confirm": string; + "continue": string; "close": string; "edit": string; "fill": string; @@ -218,8 +219,11 @@ export type I18nTranslations = { "yesDelete": string; "rename": string; "duplicate": string; + "export": string; + "import": string; "change": string; "upgrade": string; + "upgradeToLevel": string; "search": string; "loadMore": string; "collapseSidebar": string; @@ -231,23 +235,22 @@ export type I18nTranslations = { "showAllRow": string; "hideNotMatchRow": string; "more": string; + "expand": string; + "view": string; + "preview": string; + "viewAndEdit": string; + "deleteTip": string; "move": string; "turnOn": string; "exit": string; "next": string; "previous": string; "select": string; - "view": string; - "preview": string; - "viewAndEdit": string; - "continue": string; - "export": string; - "import": string; - "expand": string; - "deleteTip": string; "refresh": string; "login": string; "useTemplate": string; + "backToSpace": string; + "switchBase": string; }; "quickAction": { "title": string; @@ -256,12 +259,13 @@ export type I18nTranslations = { "password": { "setInvalid": string; }; - "non": { - "share": string; - "copy": string; - }; "template": { + "non": { + "share": string; + "copy": string; + }; "aiTitle": string; + "aiGreeting": string; "aiSubTitle": string; "guideTitle": string; "watchVideo": string; @@ -285,11 +289,6 @@ export type I18nTranslations = { "guide7": string; }; }; - "non": { - "share": string; - "copy": string; - }; - "aiGreeting": string; "useTemplateDialog": { "title": string; "description": string; @@ -341,6 +340,20 @@ export type I18nTranslations = { "addPasswordSuccess": { "title": string; }; + "deleteAccount": { + "title": string; + "desc": string; + "error": { + "title": string; + "desc": string; + "spacesError": string; + }; + "confirm": { + "title": string; + "placeholder": string; + }; + "loading": string; + }; "changeEmail": { "title": string; "desc": string; @@ -361,20 +374,6 @@ export type I18nTranslations = { "sendSuccess": string; }; }; - "deleteAccount": { - "title": string; - "desc": string; - "error": { - "title": string; - "desc": string; - "spacesError": string; - }; - "confirm": { - "title": string; - "placeholder": string; - }; - "loading": string; - }; }; "notify": { "title": string; @@ -402,14 +401,6 @@ export type I18nTranslations = { }; "integration": { "title": string; - "description": string; - "lastUsed": string; - "revoke": string; - "owner": string; - "revokeTitle": string; - "revokeDesc": string; - "scopeTitle": string; - "scopeDesc": string; "thirdPartyIntegrations": { "title": string; "description": string; @@ -444,26 +435,16 @@ export type I18nTranslations = { "desc": string; }; }; + "description": string; + "lastUsed": string; + "revoke": string; + "owner": string; + "revokeTitle": string; + "revokeDesc": string; + "scopeTitle": string; + "scopeDesc": string; }; "templateAdmin": { - "header": { - "cover": string; - "name": string; - "description": string; - "markdownDescription": string; - "category": string; - "isSystem": string; - "source": string; - "status": string; - "publishSnapshot": string; - "snapshotTime": string; - "actions": string; - "featured": string; - "createdBy": string; - "userNonExistent": string; - "usage": string; - "preview": string; - }; "title": string; "noData": string; "importing": string; @@ -487,6 +468,24 @@ export type I18nTranslations = { "browseByCategory": string; }; }; + "header": { + "cover": string; + "name": string; + "description": string; + "markdownDescription": string; + "category": string; + "isSystem": string; + "source": string; + "status": string; + "publishSnapshot": string; + "snapshotTime": string; + "actions": string; + "featured": string; + "createdBy": string; + "userNonExistent": string; + "preview": string; + "usage": string; + }; "actions": { "title": string; "publish": string; @@ -550,10 +549,12 @@ export type I18nTranslations = { "free": string; "plus": string; "pro": string; - "enterprise": string; "business": string; + "enterprise": string; }; "noResult": string; + "allNodes": string; + "noDescription": string; "untitled": string; "name": string; "description": string; @@ -640,9 +641,9 @@ export type I18nTranslations = { }; "help": { "title": string; - "apiLink": string; "appLink": string; "mainLink": string; + "apiLink": string; }; "pagePermissionChangeTip": string; "listEmptyTips": string; @@ -653,6 +654,10 @@ export type I18nTranslations = { "unavailableInPlanTips": string; "unavailableConnectionTips": string; "levelTips": string; + "enterpriseFeature": string; + "automationRequiresUpgrade": string; + "authorityMatrixRequiresUpgrade": string; + "viewPricing": string; "billable": string; "billableByAuthorityMatrix": string; "licenseExpiredGracePeriod": string; @@ -671,13 +676,10 @@ export type I18nTranslations = { "paused": string; "seatLimitExceeded": string; }; - "enterpriseFeature": string; - "automationRequiresUpgrade": string; - "authorityMatrixRequiresUpgrade": string; - "viewPricing": string; }; "admin": { "setting": { + "instanceTitle": string; "description": string; "allowSignUp": string; "allowSignUpDescription": string; @@ -694,11 +696,11 @@ export type I18nTranslations = { "brandingSettings": { "title": string; "description": string; + "brandName": string; "logo": string; "logoDescription": string; "logoUpload": string; "logoUploadDescription": string; - "brandName": string; }; "ai": { "name": string; @@ -903,7 +905,6 @@ export type I18nTranslations = { "aiGatewayDescription": string; "aiGatewayApiKey": string; }; - "instanceTitle": string; }; "action": { "enterApiKey": string; @@ -1155,6 +1156,16 @@ export type I18nTranslations = { }; }; }; + "rewardRejected": { + "title": string; + "message": string; + "buttonText": string; + }; + "rewardApproved": { + "title": string; + "message": string; + "buttonText": string; + }; }; }; "title": string; @@ -1190,8 +1201,6 @@ export type I18nTranslations = { "base": { "deleteTip": string; }; - "allNodes": string; - "noDescription": string; "noPermissionToCreateBase": string; "app": { "title": string; @@ -1199,6 +1208,139 @@ export type I18nTranslations = { "previewAppError": string; "sendErrorToAI": string; }; + "credit": { + "title": string; + "leftAmount": string; + "winFreeCredits": string; + "getCredits": string; + "winCredit": { + "title": string; + "freeCredits": string; + "guidelinesTitle": string; + "tagTeableio": string; + "minCharacters": string; + "minFollowers": string; + "limitPerWeek": string; + "postOnX": string; + "postOnLinkedIn": string; + "preFilledDraft": string; + "claimTitle": string; + "userEmail": string; + "postUrlLabel": string; + "postUrlPlaceholder": string; + "invalidUrl": string; + "claiming": string; + "claimCredits": string; + "congratulations": string; + "claimSuccess": string; + "verifying": string; + "verifyingDescription": string; + "verifyFailed": string; + "tryAgain": string; + }; + "error": { + "verificationFailed": string; + }; + }; + "reward": { + "title": string; + "rewardCredits": string; + "minCharCount": string; + "minFollowerCount": string; + "mustMention": string; + "fetchSnapshotFailed": string; + "alreadyClaimedThisWeek": string; + "manage": { + "title": string; + "description": string; + "overview": string; + "records": string; + "searchSpace": string; + "searchRecords": string; + "dateRange": string; + "from": string; + "to": string; + "totalSpaces": string; + "totalRecords": string; + "space": string; + "allSpaces": string; + "user": string; + "creator": string; + "platform": string; + "allStatuses": string; + "allPlatforms": string; + "pendingCount": string; + "approvedCount": string; + "rejectedCount": string; + "approvedAmount": string; + "consumedAmount": string; + "availableAmount": string; + "expiringSoonAmount": string; + "amount": string; + "remainingAmount": string; + "createdTime": string; + "rewardTime": string; + "expiredTime": string; + "lastModified": string; + "viewDetails": string; + "details": string; + "basicInfo": string; + "amountInfo": string; + "timeInfo": string; + "socialInfo": string; + "verifyResult": string; + "uniqueKey": string; + "verify": string; + "valid": string; + "invalid": string; + "errors": string; + "copied": string; + "openPost": string; + "noData": string; + "page": string; + "status": { + "label": string; + "pending": string; + "approved": string; + "rejected": string; + }; + }; + }; + "clickToCopyTooltip": string; + "copiedTooltip": string; + "hiddenFieldCount_one": string; + "hiddenFieldCount_other": string; + "invalidFieldMapping": string; + "sourceFieldNotFoundMapping": string; + "targetFieldNotFoundMapping": string; + "fieldTypeNotSupportedMapping": string; + "fieldSettingsNotMatchMapping": string; + "fieldSettingsLookupNotMatch": string; + "fieldSettingsLinkTableNotMatch": string; + "fieldSettingsLinkViewNotMatch": string; + "fieldTypeDifferentMapping": string; + "fieldMappingSourceTip": string; + "fieldMappingTargetTip": string; + "reset": string; + "checkAll": string; + "uncheckAll": string; + "duplicateOptionsMapping": string; + "lookupFieldInvalidMapping": string; + "noMatchedOptions": string; + "needManualSelectionMapping": string; + "targetFieldIsComputed": string; + "targetFieldIsComputedTips": string; + "emptyOption": string; + "showEmptyTip": string; + "hideEmptyTip": string; + "hideText": string; + "showText": string; + "sourceTable": string; + "sourceView": string; + "non": { + "share": string; + "copy": string; + }; }; "dashboard": { "empty": { @@ -2561,6 +2703,7 @@ export type I18nTranslations = { "lookupFieldIdInvalid": string; "formulaExpressionParseError": string; "formulaReferenceNotFound": string; + "formulaReferenceNotFieldId": string; "rollupExpressionParseError": string; "choiceNameAlreadyExists": string; "symmetricFieldIdRequired": string; @@ -2584,14 +2727,16 @@ export type I18nTranslations = { "clickCountReachedMaxCount": string; "notSupportReset": string; }; - "formulaReferenceNotFieldId": string; }; "view": { "notFound": string; + "cannotDeleteLastView": string; "defaultViewNotFound": string; "propertyParseError": string; "primaryFieldCannotBeHidden": string; "filterUnsupportedFieldType": string; + "filterInvalidOperator": string; + "filterInvalidOperatorMode": string; "sortUnsupportedFieldType": string; "groupUnsupportedFieldType": string; "anchorNotFound": string; @@ -2599,9 +2744,6 @@ export type I18nTranslations = { "shareNotEnabled": string; "shareAlreadyEnabled": string; "shareAlreadyDisabled": string; - "cannotDeleteLastView": string; - "filterInvalidOperator": string; - "filterInvalidOperatorMode": string; }; "billing": { "insufficientCredit": string; @@ -2772,6 +2914,28 @@ export type I18nTranslations = { "noProjectOrVersionFound": string; "noDeploymentUrlAvailable": string; }; + "reward": { + "notFound": string; + "unsupportedSourceType": string; + "maxClaimsReached": string; + "verificationFailed": string; + "alreadyClaimedThisWeek": string; + "invalidPostUrl": string; + "postAlreadyUsed": string; + "unsupportedPlatformUrl": string; + "unsupportedPlatform": string; + "minCharCount": string; + "minFollowerCount": string; + "mustMention": string; + "fetchTweetFailed": string; + "tweetNotFound": string; + "fetchUserFailed": string; + "xUserNotFound": string; + "fetchLinkedInPostFailed": string; + "linkedInPostNotFound": string; + "linkedInAuthorNotFound": string; + "fetchLinkedInUserFailed": string; + }; }; }; "setting": { @@ -2836,6 +3000,10 @@ export type I18nTranslations = { "pin": string; "empty": string; }; + "tooltip": { + "noPermissionToCreateBase": string; + "creatingBase": string; + }; "tip": { "delete": string; "title": string; @@ -2971,6 +3139,7 @@ export type I18nTranslations = { "system": { "notFound": { "title": string; + "description": string; }; "links": { "backToHome": string; @@ -2983,6 +3152,10 @@ export type I18nTranslations = { "title": string; "description": string; }; + "error": { + "title": string; + "description": string; + }; }; "table": { "toolbar": { diff --git a/apps/nextjs-app/package.json b/apps/nextjs-app/package.json index 66a9012329..7b25a0f4ab 100644 --- a/apps/nextjs-app/package.json +++ b/apps/nextjs-app/package.json @@ -64,6 +64,7 @@ "@types/react-syntax-highlighter": "15.5.11", "@types/react-test-renderer": "18.0.7", "@types/sharedb": "3.3.10", + "@types/canvas-confetti": "1.9.0", "@vitejs/plugin-react-swc": "3.6.0", "@vitest/coverage-v8": "2.1.5", "autoprefixer": "10.4.19", @@ -129,11 +130,10 @@ "@teable/openapi": "workspace:^", "@teable/sdk": "workspace:^", "@teable/ui-lib": "workspace:^", - "@types/canvas-confetti": "1.9.0", + "canvas-confetti": "1.9.4", "allotment": "1.20.0", "axios": "1.7.7", "buffer": "6.0.3", - "canvas-confetti": "1.9.4", "class-variance-authority": "0.7.0", "date-fns": "4.1.0", "date-fns-tz": "3.2.0", diff --git a/apps/nextjs-app/public/images/layout/error-dark.png b/apps/nextjs-app/public/images/layout/error-dark.png new file mode 100644 index 0000000000..2db90b5b82 Binary files /dev/null and b/apps/nextjs-app/public/images/layout/error-dark.png differ diff --git a/apps/nextjs-app/public/images/layout/error-light.png b/apps/nextjs-app/public/images/layout/error-light.png new file mode 100644 index 0000000000..572ca5860d Binary files /dev/null and b/apps/nextjs-app/public/images/layout/error-light.png differ diff --git a/apps/nextjs-app/public/images/layout/not-found-dark.png b/apps/nextjs-app/public/images/layout/not-found-dark.png new file mode 100644 index 0000000000..b4746243b8 Binary files /dev/null and b/apps/nextjs-app/public/images/layout/not-found-dark.png differ diff --git a/apps/nextjs-app/public/images/layout/not-found-light.png b/apps/nextjs-app/public/images/layout/not-found-light.png new file mode 100644 index 0000000000..35c36b913f Binary files /dev/null and b/apps/nextjs-app/public/images/layout/not-found-light.png differ diff --git a/apps/nextjs-app/public/images/layout/permission-dark.png b/apps/nextjs-app/public/images/layout/permission-dark.png new file mode 100644 index 0000000000..1c0dff1c69 Binary files /dev/null and b/apps/nextjs-app/public/images/layout/permission-dark.png differ diff --git a/apps/nextjs-app/public/images/layout/permission-light.png b/apps/nextjs-app/public/images/layout/permission-light.png new file mode 100644 index 0000000000..48f0d118e2 Binary files /dev/null and b/apps/nextjs-app/public/images/layout/permission-light.png differ diff --git a/apps/nextjs-app/public/images/layout/upgrade-dark.png b/apps/nextjs-app/public/images/layout/upgrade-dark.png new file mode 100644 index 0000000000..517746b08c Binary files /dev/null and b/apps/nextjs-app/public/images/layout/upgrade-dark.png differ diff --git a/apps/nextjs-app/public/images/layout/upgrade-light.png b/apps/nextjs-app/public/images/layout/upgrade-light.png new file mode 100644 index 0000000000..511c19ee07 Binary files /dev/null and b/apps/nextjs-app/public/images/layout/upgrade-light.png differ diff --git a/apps/nextjs-app/src/features/app/blocks/admin/setting/SettingPage.tsx b/apps/nextjs-app/src/features/app/blocks/admin/setting/SettingPage.tsx index 3ade690afd..a4f182a640 100644 --- a/apps/nextjs-app/src/features/app/blocks/admin/setting/SettingPage.tsx +++ b/apps/nextjs-app/src/features/app/blocks/admin/setting/SettingPage.tsx @@ -26,10 +26,11 @@ import { scrollToTarget } from './utils'; export interface ISettingPageProps { settingServerData?: ISettingVo; + rewardManage?: React.ReactNode; } export const SettingPage = (props: ISettingPageProps) => { - const { settingServerData } = props; + const { settingServerData, rewardManage } = props; const queryClient = useQueryClient(); const { t } = useTranslation('common'); @@ -264,6 +265,8 @@ export const SettingPage = (props: ISettingPageProps) => { )} + {rewardManage} + {/* email config */}

{t('email.config')}

diff --git a/apps/nextjs-app/src/features/app/blocks/base/base-side-bar/BaseSideBar.tsx b/apps/nextjs-app/src/features/app/blocks/base/base-side-bar/BaseSideBar.tsx index 546de1c643..77be45dd68 100644 --- a/apps/nextjs-app/src/features/app/blocks/base/base-side-bar/BaseSideBar.tsx +++ b/apps/nextjs-app/src/features/app/blocks/base/base-side-bar/BaseSideBar.tsx @@ -1,13 +1,23 @@ // import { TableList } from '../../table-list/TableList'; +import { CollaboratorType } from '@teable/openapi'; +import { useBase } from '@teable/sdk/hooks'; import { BaseNodeTree } from './BaseNodeTree'; import { BasePageRouter } from './BasePageRouter'; -export const BaseSideBar = () => { +export const BaseSideBar = (props: { + renderWinFreeCredit?: (spaceId: string) => React.ReactNode; +}) => { + const { renderWinFreeCredit } = props; + const base = useBase(); + const isSpaceCollaborator = base.collaboratorType === CollaboratorType.Space; return ( <> {/* */} - +
+ +
+ {isSpaceCollaborator && renderWinFreeCredit && renderWinFreeCredit(base.spaceId)} ); }; diff --git a/apps/nextjs-app/src/features/app/blocks/base/base-side-bar/BaseSidebarHeaderLeft.tsx b/apps/nextjs-app/src/features/app/blocks/base/base-side-bar/BaseSidebarHeaderLeft.tsx index 333fa254ed..3d6a9db273 100644 --- a/apps/nextjs-app/src/features/app/blocks/base/base-side-bar/BaseSidebarHeaderLeft.tsx +++ b/apps/nextjs-app/src/features/app/blocks/base/base-side-bar/BaseSidebarHeaderLeft.tsx @@ -1,19 +1,166 @@ -import { useMutation, useQueryClient } from '@tanstack/react-query'; +import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import { hasPermission } from '@teable/core'; -import { ChevronsLeft, ChevronDown, Menu } from '@teable/icons'; -import { CollaboratorType, deleteBase, permanentDeleteBase, updateBase } from '@teable/openapi'; +import { ChevronsLeft, ChevronDown, Database, HelpCircle, Pencil } from '@teable/icons'; +import { CollaboratorType, getBaseList, getSharedBase, updateBase } from '@teable/openapi'; import { ReactQueryKeys } from '@teable/sdk/config'; import { useBase } from '@teable/sdk/hooks'; import { useIsTemplate } from '@teable/sdk/hooks/use-is-template'; -import { Button, cn, Input } from '@teable/ui-lib'; +import { + cn, + DropdownMenu, + DropdownMenuItem, + DropdownMenuContent, + DropdownMenuTrigger, + DropdownMenuSub, + DropdownMenuSubContent, + DropdownMenuSubTrigger, + Input, + DropdownMenuSeparator, +} from '@teable/ui-lib'; +import { ArrowLeft, Send } from 'lucide-react'; +import Link from 'next/link'; import { useRouter } from 'next/router'; +import { useTranslation } from 'next-i18next'; import { useRef, useState } from 'react'; import { TeableLogo } from '@/components/TeableLogo'; import { Emoji } from '@/features/app/components/emoji/Emoji'; -import { BaseActionTrigger } from '../../space/component/BaseActionTrigger'; -import { BaseListTrigger } from '../../space/component/BaseListTrigger'; +import { useIsCloud } from '@/features/app/hooks/useIsCloud'; +import { tableConfig } from '@/features/i18n/table.config'; +import { PublishBaseDialog } from '../../table/table-header/publish-base/PublishBaseDialog'; -export const BaseSidebarHeaderLeft = () => { +const BaseDropdownMenu = ({ + children, + showRename, + onRename, + backSpace, + creditUsage, + spaceId, + collaboratorType, + currentBaseId, + disabled, +}: { + children: React.ReactNode; + showRename: boolean; + onRename: () => void; + backSpace: () => void; + spaceId: string; + creditUsage?: React.ReactNode; + collaboratorType?: CollaboratorType; + currentBaseId: string; + disabled?: boolean; +}) => { + const { t } = useTranslation(tableConfig.i18nNamespaces); + const isCloud = useIsCloud(); + const [open, setOpen] = useState(false); + + const isSpaceCollaborator = collaboratorType === CollaboratorType.Space; + const { data: spaceBases } = useQuery({ + queryKey: ReactQueryKeys.baseList(spaceId), + queryFn: ({ queryKey }) => getBaseList({ spaceId: queryKey[1] }).then((res) => res.data), + enabled: open && isSpaceCollaborator, + }); + + const { data: sharedBases } = useQuery({ + queryKey: ReactQueryKeys.getSharedBase(), + queryFn: () => getSharedBase().then((res) => res.data), + enabled: open && collaboratorType === CollaboratorType.Base, + }); + + const bases = spaceBases || sharedBases; + + return ( + + + {children} + + e.stopPropagation()} + > + +
+ + {t('common:actions.backToSpace')} +
+
+ + {isCloud && isSpaceCollaborator && creditUsage && ( + <> +
{creditUsage}
+ + + )} + + +
+ + {t('common:actions.switchBase')} +
+
+ + {bases?.map((base) => ( + + + + {base.icon ? ( + + ) : ( + + )} + + + {base.name} + + + + ))} + +
+ {showRename && ( + +
+ + {t('actions.rename')} +
+
+ )} + setOpen(false)} closeOnSuccess={false}> + e.preventDefault()}> +
+ + {t('space:publishBase.publishToCommunity')} +
+
+
+ + + + + + {t('help.title')} + + +
+
+ ); +}; + +export const BaseSidebarHeaderLeft = ({ creditUsage }: { creditUsage?: React.ReactNode }) => { const base = useBase(); const router = useRouter(); const [renaming, setRenaming] = useState(); @@ -30,17 +177,6 @@ export const BaseSidebarHeaderLeft = () => { }, }); - const { mutate: deleteBaseMutator } = useMutation({ - mutationFn: ({ baseId, permanent }: { baseId: string; permanent?: boolean }) => - permanent ? permanentDeleteBase(baseId) : deleteBase(baseId), - onSuccess: () => { - router.push({ - pathname: '/space/[spaceId]', - query: { spaceId: base.spaceId }, - }); - }, - }); - const toggleRenameBase = async () => { if (baseName && baseName !== base.name) { await updateBaseMutator({ @@ -57,8 +193,6 @@ export const BaseSidebarHeaderLeft = () => { }; const hasUpdatePermission = hasPermission(base.role, 'base|update'); - const hasDeletePermission = hasPermission(base.role, 'base|delete'); - const hasMovePermission = hasPermission(base.role, 'space|create'); const backSpace = () => { if (isTemplate) { @@ -126,35 +260,29 @@ export const BaseSidebarHeaderLeft = () => { /> ) : ( -

- {base.name} -

- )} - {!isTemplate && ( - deleteBaseMutator({ baseId: base.id, permanent })} onRename={onRename} - align="start" + spaceId={base.spaceId} + creditUsage={creditUsage} + collaboratorType={base.collaboratorType} + currentBaseId={base.id} + disabled={isTemplate} > - - +
+ + {base.name} + + {!isTemplate && } +
+ )}
- {!isTemplate && ( - - - - )} ); }; diff --git a/apps/nextjs-app/src/features/app/blocks/base/base-side-bar/QuickAction.tsx b/apps/nextjs-app/src/features/app/blocks/base/base-side-bar/QuickAction.tsx index be80d0c56e..e57a35eed4 100644 --- a/apps/nextjs-app/src/features/app/blocks/base/base-side-bar/QuickAction.tsx +++ b/apps/nextjs-app/src/features/app/blocks/base/base-side-bar/QuickAction.tsx @@ -62,9 +62,9 @@ export const QuickAction = ({ children }: React.PropsWithChildren) => { variant="outline" onClick={() => setOpen(true)} > - {children} + {children} {isHydrated && ( - + {modKeyStr} K diff --git a/apps/nextjs-app/src/features/app/blocks/space-setting/SpaceInnerSettingModal.tsx b/apps/nextjs-app/src/features/app/blocks/space-setting/SpaceInnerSettingModal.tsx index 0c9940d2fc..dcb2f022e4 100644 --- a/apps/nextjs-app/src/features/app/blocks/space-setting/SpaceInnerSettingModal.tsx +++ b/apps/nextjs-app/src/features/app/blocks/space-setting/SpaceInnerSettingModal.tsx @@ -9,28 +9,55 @@ import { } from '@teable/ui-lib/shadcn'; import { Settings, Users } from 'lucide-react'; import { useTranslation } from 'next-i18next'; -import { useMemo, useState } from 'react'; +import { useCallback, useEffect, useMemo, useState } from 'react'; import { spaceConfig } from '@/features/i18n/space.config'; import { CollaboratorPage } from './collaborator'; import { GeneralPage } from './general'; interface ISpaceInnerSettingModalProps { + open?: boolean; + setOpen?: (open: boolean) => void; + defaultTab?: SettingTab; children: React.ReactNode; } -enum SettingTab { +export enum SettingTab { General = 'general', Collaborator = 'collaborator', } export const SpaceInnerSettingModal = (props: ISpaceInnerSettingModalProps) => { - const { children } = props; + const { + children, + open: controlledOpen, + setOpen: controlledSetOpen, + defaultTab = SettingTab.General, + } = props; const { t } = useTranslation(spaceConfig.i18nNamespaces); - const [open, setOpen] = useState(false); + const [internalOpen, setInternalOpen] = useState(false); + const isControlled = controlledOpen !== undefined; + const open = isControlled ? controlledOpen : internalOpen; + const setOpen = useCallback( + (value: boolean) => { + if (controlledSetOpen) { + controlledSetOpen(value); + } + if (!isControlled) { + setInternalOpen(value); + } + }, + [controlledSetOpen, isControlled, setInternalOpen] + ); + + const [tab, setTab] = useState(defaultTab); + useEffect(() => { + if (open) { + setTab(defaultTab); + } + }, [open, defaultTab]); - const [tab, setTab] = useState(SettingTab.General); const tabList = useMemo(() => { return [ { @@ -79,7 +106,10 @@ export const SpaceInnerSettingModal = (props: ISpaceInnerSettingModalProps) => { return ( {children} - + e.preventDefault()} + > {content} diff --git a/apps/nextjs-app/src/features/app/blocks/space/component/BaseListTrigger.tsx b/apps/nextjs-app/src/features/app/blocks/space/component/BaseListTrigger.tsx deleted file mode 100644 index 47e27d68cd..0000000000 --- a/apps/nextjs-app/src/features/app/blocks/space/component/BaseListTrigger.tsx +++ /dev/null @@ -1,58 +0,0 @@ -import { useQuery } from '@tanstack/react-query'; -import { Database } from '@teable/icons'; -import { CollaboratorType, getBaseList, getSharedBase } from '@teable/openapi'; -import { ReactQueryKeys } from '@teable/sdk/config'; -import { - DropdownMenu, - DropdownMenuContent, - DropdownMenuItem, - DropdownMenuTrigger, -} from '@teable/ui-lib/shadcn'; -import Link from 'next/link'; -import React from 'react'; - -export const BaseListTrigger = ({ - collaboratorType, - spaceId, - children, -}: { - collaboratorType?: CollaboratorType; - spaceId: string; - children: React.ReactNode; -}) => { - const { data: spaceBases } = useQuery({ - queryKey: ReactQueryKeys.baseList(spaceId), - queryFn: ({ queryKey }) => getBaseList({ spaceId: queryKey[1] }).then((res) => res.data), - enabled: collaboratorType !== CollaboratorType.Base, - }); - - const { data: sharedBases } = useQuery({ - queryKey: ReactQueryKeys.getSharedBase(), - queryFn: () => getSharedBase().then((res) => res.data), - enabled: collaboratorType === CollaboratorType.Base, - }); - - const bases = spaceBases || sharedBases; - - if (!bases) return null; - - return ( - - {children} - - {bases.map((base) => ( - - - {base.icon ? base.icon : } - {base.name} - - - ))} - - - ); -}; diff --git a/apps/nextjs-app/src/features/app/blocks/space/component/SpaceActionTrigger.tsx b/apps/nextjs-app/src/features/app/blocks/space/component/SpaceActionTrigger.tsx index a1a0d9af14..58906e9cb6 100644 --- a/apps/nextjs-app/src/features/app/blocks/space/component/SpaceActionTrigger.tsx +++ b/apps/nextjs-app/src/features/app/blocks/space/component/SpaceActionTrigger.tsx @@ -1,4 +1,4 @@ -import { Pencil, Trash2, Import } from '@teable/icons'; +import { Trash2, Import, Settings, Pencil } from '@teable/icons'; import type { IGetSpaceVo } from '@teable/openapi'; import { DropdownMenu, @@ -8,15 +8,17 @@ import { DropdownMenuTrigger, } from '@teable/ui-lib/shadcn'; import { useTranslation } from 'next-i18next'; -import React from 'react'; +import React, { useCallback, useState } from 'react'; import { DeleteSpaceConfirm } from '@/features/app/components/space/DeleteSpaceConfirm'; import { spaceConfig } from '@/features/i18n/space.config'; +import { SpaceInnerSettingModal, SettingTab } from '../../space-setting/SpaceInnerSettingModal'; interface ISpaceActionTrigger { space: IGetSpaceVo; showRename?: boolean; showDelete?: boolean; showImportBase?: boolean; + showSettings?: boolean; onRename?: () => void; onDelete?: () => void; onPermanentDelete?: () => void; @@ -37,6 +39,7 @@ export const SpaceActionTrigger: React.FC { + setOpen?.(false); + setSettingModalOpen(true); + }, [setOpen, setSettingModalOpen]); + + if (!showDelete && !showRename && !showSettings) { return null; } + return ( <> @@ -64,6 +74,12 @@ export const SpaceActionTrigger: React.FC )} + {showSettings && ( + + + {t('space:spaceSetting.title')} + + )} {showDelete && ( <> @@ -83,6 +99,13 @@ export const SpaceActionTrigger: React.FC + + + ); }; diff --git a/apps/nextjs-app/src/features/app/blocks/space/space-side-bar/SpaceInnerSideBar.tsx b/apps/nextjs-app/src/features/app/blocks/space/space-side-bar/SpaceInnerSideBar.tsx index 33501f0769..faf6f20e5a 100644 --- a/apps/nextjs-app/src/features/app/blocks/space/space-side-bar/SpaceInnerSideBar.tsx +++ b/apps/nextjs-app/src/features/app/blocks/space/space-side-bar/SpaceInnerSideBar.tsx @@ -5,6 +5,12 @@ import { createBase, getSpaceById } from '@teable/openapi'; import { ReactQueryKeys } from '@teable/sdk/config'; import { cn } from '@teable/ui-lib/shadcn'; import { Button } from '@teable/ui-lib/shadcn/ui/button'; +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from '@teable/ui-lib/shadcn/ui/tooltip'; import Link from 'next/link'; import { useParams } from 'next/navigation'; import { useRouter } from 'next/router'; @@ -18,8 +24,9 @@ import { PinList } from './PinList'; export const SpaceInnerSideBar = (props: { renderSettingModal?: (children: React.ReactNode) => React.ReactNode; + renderWinFreeCredit?: (spaceId: string) => React.ReactNode; }) => { - const { renderSettingModal } = props; + const { renderSettingModal, renderWinFreeCredit } = props; const router = useRouter(); const { t } = useTranslation(spaceConfig.i18nNamespaces); const { spaceId } = useParams<{ spaceId: string }>(); @@ -70,16 +77,29 @@ export const SpaceInnerSideBar = (props: {
{space && (
- + + + + + + {(!canCreateBase || createBaseLoading) && ( + + {!canCreateBase + ? t('space:tooltip.noPermissionToCreateBase') + : t('space:tooltip.creatingBase')} + + )} + +
)}
    @@ -149,6 +169,7 @@ export const SpaceInnerSideBar = (props: {
    + {renderWinFreeCredit && renderWinFreeCredit(spaceId)} ); }; diff --git a/apps/nextjs-app/src/features/app/blocks/space/space-side-bar/SpaceSwitcher.tsx b/apps/nextjs-app/src/features/app/blocks/space/space-side-bar/SpaceSwitcher.tsx index 5a54e7e12d..5568c86e68 100644 --- a/apps/nextjs-app/src/features/app/blocks/space/space-side-bar/SpaceSwitcher.tsx +++ b/apps/nextjs-app/src/features/app/blocks/space/space-side-bar/SpaceSwitcher.tsx @@ -1,6 +1,6 @@ import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import { getUniqName } from '@teable/core'; -import { Admin, Check, ChevronDown, Database, Plus, Settings, Trash2 } from '@teable/icons'; +import { Check, ChevronDown, Database, Plus, Settings, ShieldUser, Trash2 } from '@teable/icons'; import { createSpace, getSubscriptionSummaryList, @@ -26,27 +26,61 @@ import { PopoverContent, PopoverTrigger, } from '@teable/ui-lib/shadcn'; +import { Building2 } from 'lucide-react'; import Link from 'next/link'; import { useParams } from 'next/navigation'; import { useRouter } from 'next/router'; import { useTranslation } from 'next-i18next'; -import { useMemo, useState } from 'react'; +import type { ReactNode } from 'react'; +import { useCallback, useMemo, useState } from 'react'; import { useIsCloud } from '@/features/app/hooks/useIsCloud'; import { spaceConfig } from '@/features/i18n/space.config'; import { Level } from '../../../components/billing/Level'; import { SpaceAvatar } from '../../../components/space/SpaceAvatar'; +import { SpaceInnerSettingModal as SpaceInnerSettingModalComponent } from '../../space-setting'; +import { SettingTab } from '../../space-setting/SpaceInnerSettingModal'; import { useSpaceList } from '../hooks'; import { usePinMap } from '../usePinMap'; import { StarButton } from './StarButton'; -export const SpaceSwitcher = () => { +interface ISpaceSwitcherProps { + open?: boolean; + setOpen?: (open: boolean) => void; + upgradeTip?: ReactNode; + creditUsage?: ReactNode; + spaceInnerSettingModal?: ReactNode; +} + +export const SpaceSwitcher = (props: ISpaceSwitcherProps) => { + const { + open: controlledOpen, + setOpen: controlledSetOpen, + upgradeTip, + creditUsage, + spaceInnerSettingModal, + } = props; const router = useRouter(); const { t } = useTranslation(spaceConfig.i18nNamespaces); const { user } = useSession(); const isCloud = useIsCloud(); const queryClient = useQueryClient(); - const [open, setOpen] = useState(false); + const [internalOpen, setInternalOpen] = useState(false); + const isControlled = controlledOpen !== undefined; + const open = isControlled ? controlledOpen : internalOpen; + const setOpen = useCallback( + (value: boolean) => { + if (controlledSetOpen) { + controlledSetOpen(value); + } + if (!isControlled) { + setInternalOpen(value); + } + }, + [controlledSetOpen, isControlled, setInternalOpen] + ); + + const [settingModalOpen, setSettingModalOpen] = useState(false); const [showCreateDialog, setShowCreateDialog] = useState(false); const [spaceName, setSpaceName] = useState(''); const [highlightedValue, setHighlightedValue] = useState(); @@ -73,6 +107,13 @@ export const SpaceSwitcher = () => { return spaceList?.find((space) => space.id === currentSpaceId); }, [spaceList, currentSpaceId]); + const sortedSpaceList = useMemo(() => { + if (!spaceList || !currentSpaceId) return spaceList; + const currentSpaceItem = spaceList.find((s) => s.id === currentSpaceId); + if (!currentSpaceItem) return spaceList; + return [currentSpaceItem, ...spaceList.filter((s) => s.id !== currentSpaceId)]; + }, [spaceList, currentSpaceId]); + const organization = user?.organization; const { mutate: addSpace, isLoading } = useMutation({ @@ -133,7 +174,11 @@ export const SpaceSwitcher = () => { - + e.preventDefault()} + > { return 0; }} > -
    -

    - {t('space:allSpaces')} ({spaceList?.length || 0}) -

    - -
    + {isCloud && (upgradeTip || creditUsage) && ( +
    + {upgradeTip} + {creditUsage} +
    + )} - - {t('common:noResult')} +
    +
    + +
    - - {spaceList?.map((space) => { - const isSelected = space.id === currentSpaceId; - const subscription = subscriptionMap.get(space.id); - const isPinned = pinMap?.[space.id]; + + {t('common:noResult')} - return ( - handleSelectSpace(space)} - className={cn('group flex items-center gap-2 rounded-md h-10')} - > -
    - - {space.name} - {isCloud && } - -
    + + {sortedSpaceList?.map((space) => { + const isSelected = space.id === currentSpaceId; + const subscription = subscriptionMap.get(space.id); + const spaceIsPinned = pinMap?.[space.id]; -
    - {isSelected && } -
    -
    - ); - })} -
    - + return ( + handleSelectSpace(space)} + className={cn('group flex items-center gap-2 rounded-md h-10')} + > +
    + + {space.name} + + {isCloud && } +
    -
    - +
    + {isSelected && } +
    + + ); + })} + + + +
    + +
    @@ -214,21 +264,22 @@ export const SpaceSwitcher = () => { target="_blank" rel="noopener noreferrer" onClick={() => setOpen(false)} - className="flex h-9 items-center gap-2 rounded-md p-2 hover:bg-accent" + className="flex h-8 items-center gap-2 rounded-md px-2 hover:bg-accent" > - {t('space:sharedBase.title')} + {t('space:sharedBase.title')} + {user?.isAdmin && ( setOpen(false)} - className="flex h-9 items-center gap-2 rounded-md p-2 hover:bg-accent" + className="flex h-8 items-center gap-2 rounded-md px-2 hover:bg-accent" > - - {t('common:noun.adminPanel')} + + {t('common:noun.adminPanel')} )} @@ -238,10 +289,10 @@ export const SpaceSwitcher = () => { target="_blank" rel="noopener noreferrer" onClick={() => setOpen(false)} - className="flex h-9 items-center gap-2 rounded-md p-2 hover:bg-accent" + className="flex h-8 items-center gap-2 rounded-md px-2 hover:bg-accent" > - - {t('common:noun.organizationPanel')} + + {t('common:noun.organizationPanel')} )} @@ -250,10 +301,10 @@ export const SpaceSwitcher = () => { target="_blank" rel="noopener noreferrer" onClick={() => setOpen(false)} - className="flex h-9 items-center gap-2 rounded-md p-2 hover:bg-accent" + className="flex h-8 items-center gap-2 rounded-md p-2 hover:bg-accent" > - {t('common:trash.spaceTrash')} + {t('common:trash.spaceTrash')}
    @@ -292,6 +343,18 @@ export const SpaceSwitcher = () => {
} /> + + {spaceInnerSettingModal ? ( + spaceInnerSettingModal + ) : ( + + + + )} ); }; diff --git a/apps/nextjs-app/src/features/app/components/SideBarFooter.tsx b/apps/nextjs-app/src/features/app/components/SideBarFooter.tsx index 019c3ed7ad..2a80b45b1e 100644 --- a/apps/nextjs-app/src/features/app/components/SideBarFooter.tsx +++ b/apps/nextjs-app/src/features/app/components/SideBarFooter.tsx @@ -47,13 +47,13 @@ export const SideBarFooter: React.FC = () => { } return ( -
+
diff --git a/apps/nextjs-app/src/features/app/components/sidebar/Sidebar.tsx b/apps/nextjs-app/src/features/app/components/sidebar/Sidebar.tsx index 9277967c23..ffe15ba7d6 100644 --- a/apps/nextjs-app/src/features/app/components/sidebar/Sidebar.tsx +++ b/apps/nextjs-app/src/features/app/components/sidebar/Sidebar.tsx @@ -1,15 +1,18 @@ import { ChevronsLeft } from '@teable/icons'; -import { useIsMobile, useIsTemplate } from '@teable/sdk'; +import { useIsHydrated, useIsMobile, useIsTemplate } from '@teable/sdk'; import { Button, cn } from '@teable/ui-lib'; +import { Resizable } from 're-resizable'; import type { FC, PropsWithChildren, ReactNode } from 'react'; -import { useEffect, useState } from 'react'; +import { useCallback, useMemo, useState } from 'react'; import { useHotkeys } from 'react-hotkeys-hook'; -import { useMedia } from 'react-use'; -import { SIDE_BAR_WIDTH } from '../toggle-side-bar/constant'; +import { + MAX_SIDE_BAR_WIDTH, + MIN_SIDE_BAR_WIDTH, + SIDE_BAR_WIDTH, +} from '../toggle-side-bar/constant'; import { HoverWrapper } from '../toggle-side-bar/HoverWrapper'; import { SheetWrapper } from '../toggle-side-bar/SheetWrapper'; import { SidebarHeader } from './SidebarHeader'; -import { useChatPanelStore } from './useChatPanelStore'; import { useSidebarStore } from './useSidebarStore'; interface ISidebarProps { @@ -17,90 +20,132 @@ interface ISidebarProps { className?: string; } +const useSidebar = () => { + const isTemplate = useIsTemplate(); + const [isVisible, setVisible] = useState(true); + const [width, setWidth] = useState(SIDE_BAR_WIDTH); + const storedSidebarStore = useSidebarStore(); + return useMemo(() => { + if (isTemplate) { + return { + isVisible, + setVisible, + setWidth, + width, + }; + } + return storedSidebarStore; + }, [isVisible, setVisible, setWidth, width, isTemplate, storedSidebarStore]); +}; + export const Sidebar: FC> = (props) => { const { headerLeft, children, className } = props; const isMobile = useIsMobile(); - const [leftVisible, setLeftVisible] = useState(true); - const isTemplate = useIsTemplate(); - const isLargeScreen = useMedia('(min-width: 1024px)'); - const { setVisible } = useSidebarStore(); + const { isVisible, setVisible, setWidth, width } = useSidebar(); + const isHydrated = useIsHydrated(); - const { status } = useChatPanelStore(); + const toggleSidebar = useCallback(() => { + setVisible(!isVisible); + }, [isVisible, setVisible]); - const isExpanded = status === 'expanded'; + useHotkeys('meta+b', toggleSidebar, [toggleSidebar]); - useHotkeys(`meta+b`, () => { - setVisible(!leftVisible); - }); + const sidebarClassName = cn( + 'group/sidebar flex size-full flex-col overflow-hidden bg-background', + className + ); - useEffect(() => { - setVisible(leftVisible); - }, [leftVisible, setVisible]); + const sidebarContent = useMemo( + () => ( + <> + + {children} + + ), + [headerLeft, children, toggleSidebar] + ); - useEffect(() => { - if (!isTemplate) { - setLeftVisible(isLargeScreen); - } - }, [isLargeScreen, isTemplate]); + if (isMobile) { + return ( + +
+ + {children} +
+
+ ); + } - return ( - <> - {isMobile ? ( - -
+ // Collapsed state: show trigger button with hover panel + if (!isVisible) { + return ( + + + + + +
e.preventDefault()}> {children}
- - ) : ( -
e.preventDefault()} - > -
- setLeftVisible(!leftVisible)} /> - {leftVisible && children} -
-
- )} +
+
+ ); + } - {!isMobile && !leftVisible && ( - - - - - -
e.preventDefault()} - > - - {children} -
-
-
- )} - + if (!isHydrated) { + return ( +
e.preventDefault()} + > +
{sidebarContent}
+
+ ); + } + + return ( + { + const newWidth = parseInt(ref.style.width, 10); + if (!isNaN(newWidth)) { + if (newWidth <= MIN_SIDE_BAR_WIDTH) { + setVisible(false); + } else { + setWidth(newWidth); + } + } + }} + handleClasses={{ right: 'group' }} + handleStyles={{ + right: { + width: '6px', + right: '-6px', + }, + }} + handleComponent={{ + right: ( +
+ ), + }} + > +
e.preventDefault()}> + {sidebarContent} +
+ ); }; diff --git a/apps/nextjs-app/src/features/app/components/sidebar/useSidebarStore.ts b/apps/nextjs-app/src/features/app/components/sidebar/useSidebarStore.ts index cf52aec65f..2ca00ef903 100644 --- a/apps/nextjs-app/src/features/app/components/sidebar/useSidebarStore.ts +++ b/apps/nextjs-app/src/features/app/components/sidebar/useSidebarStore.ts @@ -1,10 +1,25 @@ +import { LocalStorageKeys } from '@teable/sdk'; import { create } from 'zustand'; +import { persist } from 'zustand/middleware'; +import { SIDE_BAR_WIDTH } from '../toggle-side-bar/constant'; + interface ISidebarState { isVisible: boolean; setVisible: (isVisible: boolean) => void; + width: number; + setWidth: (width: number) => void; } -export const useSidebarStore = create()((set) => ({ - isVisible: false, - setVisible: (isVisible: boolean) => set({ isVisible }), -})); +export const useSidebarStore = create()( + persist( + (set) => ({ + isVisible: true, + width: SIDE_BAR_WIDTH, + setVisible: (isVisible: boolean) => set((state) => ({ ...state, isVisible })), + setWidth: (width: number) => set((state) => ({ ...state, width })), + }), + { + name: LocalStorageKeys.Sidebar, + } + ) +); diff --git a/apps/nextjs-app/src/features/app/components/space/SpaceActionBar.tsx b/apps/nextjs-app/src/features/app/components/space/SpaceActionBar.tsx index 3f63b9f90c..4db9323d04 100644 --- a/apps/nextjs-app/src/features/app/components/space/SpaceActionBar.tsx +++ b/apps/nextjs-app/src/features/app/components/space/SpaceActionBar.tsx @@ -103,7 +103,8 @@ export const SpaceActionBar: React.FC = (props) => { { - let hash = 0; - for (let i = 0; i < str.length; i++) { - hash = str.charCodeAt(i) + ((hash << 5) - hash); - } - return AVATAR_COLORS[Math.abs(hash) % AVATAR_COLORS.length]; -}; - interface ISpaceAvatarProps { name: string; className?: string; } export const SpaceAvatar = ({ name, className }: ISpaceAvatarProps) => { - const bgColor = getColorFromString(name); const initial = name?.charAt(0).toUpperCase() || '?'; return ( - + {initial} diff --git a/apps/nextjs-app/src/features/app/components/toggle-side-bar/constant.ts b/apps/nextjs-app/src/features/app/components/toggle-side-bar/constant.ts index 22f111dbeb..abb124cb83 100644 --- a/apps/nextjs-app/src/features/app/components/toggle-side-bar/constant.ts +++ b/apps/nextjs-app/src/features/app/components/toggle-side-bar/constant.ts @@ -1 +1,3 @@ export const SIDE_BAR_WIDTH = 288; +export const MIN_SIDE_BAR_WIDTH = 150; +export const MAX_SIDE_BAR_WIDTH = 480; diff --git a/apps/nextjs-app/src/features/app/hooks/useBillingLevelConfig.ts b/apps/nextjs-app/src/features/app/hooks/useBillingLevelConfig.ts index 481b97a350..ce1d129cab 100644 --- a/apps/nextjs-app/src/features/app/hooks/useBillingLevelConfig.ts +++ b/apps/nextjs-app/src/features/app/hooks/useBillingLevelConfig.ts @@ -11,20 +11,21 @@ export const useBillingLevelConfig = (productLevel?: BillingProductLevel) => { [BillingProductLevel.Free]: { name: t('level.free'), description: t('billing.levelTips', { level: t('level.free') }), - tagCls: 'bg-gray-200 dark:bg-gray-700 text-gray-600 dark:text-white', - upgradeTagCls: 'border border-gray-200 dark:border-gray-700 text-gray-600 dark:text-white', + tagCls: 'bg-gray-900/10 dark:bg-white/10 text-gray-600 dark:text-white/80', + upgradeTagCls: + 'border border-gray-900/10 dark:border-white/10 text-gray-600 dark:text-white', }, [BillingProductLevel.Pro]: { name: t('level.pro'), description: t('billing.levelTips', { level: t('level.pro') }), - tagCls: 'bg-emerald-200 dark:bg-emerald-700 text-emerald-600 dark:text-white', + tagCls: 'bg-emerald-100 dark:bg-emerald-500/20 text-emerald-600 dark:text-emerald-400', upgradeTagCls: 'border border-emerald-200 dark:border-emerald-700 text-emerald-600', }, [BillingProductLevel.Business]: { name: t('level.business'), description: t('billing.levelTips', { level: t('level.business') }), - tagCls: 'bg-emerald-200 dark:bg-emerald-700 text-emerald-600 dark:text-white', - upgradeTagCls: 'border border-emerald-200 dark:border-emerald-700 text-emerald-600', + tagCls: 'bg-blue-100 dark:bg-blue-500/20 text-blue-600 dark:text-blue-400', + upgradeTagCls: 'border border-blue-200 dark:border-blue-700 text-blue-600', }, [BillingProductLevel.Enterprise]: { name: t('level.enterprise'), diff --git a/apps/nextjs-app/src/features/app/hooks/useSetting.ts b/apps/nextjs-app/src/features/app/hooks/useSetting.ts index 5666718bf8..d4c65a5df8 100644 --- a/apps/nextjs-app/src/features/app/hooks/useSetting.ts +++ b/apps/nextjs-app/src/features/app/hooks/useSetting.ts @@ -18,6 +18,7 @@ export const useSetting = () => { webSearchEnabled = false, appGenerationEnabled = false, createdTime, + enableCreditReward = false, } = setting ?? {}; return { @@ -28,6 +29,7 @@ export const useSetting = () => { webSearchEnabled, appGenerationEnabled, createdTime, + enableCreditReward, }; }; diff --git a/apps/nextjs-app/src/features/app/layouts/BaseLayout.tsx b/apps/nextjs-app/src/features/app/layouts/BaseLayout.tsx index 5a0b40b2f0..213324331f 100644 --- a/apps/nextjs-app/src/features/app/layouts/BaseLayout.tsx +++ b/apps/nextjs-app/src/features/app/layouts/BaseLayout.tsx @@ -56,7 +56,7 @@ export const BaseLayout: React.FC<{
}> -
+
diff --git a/apps/nextjs-app/src/features/app/layouts/TemplateBaseLayout.tsx b/apps/nextjs-app/src/features/app/layouts/TemplateBaseLayout.tsx index 6185240adb..bfd16181be 100644 --- a/apps/nextjs-app/src/features/app/layouts/TemplateBaseLayout.tsx +++ b/apps/nextjs-app/src/features/app/layouts/TemplateBaseLayout.tsx @@ -86,7 +86,7 @@ export const TemplateBaseLayout = ({
}> -
+
diff --git a/apps/nextjs-app/src/features/system/pages/ErrorPage.tsx b/apps/nextjs-app/src/features/system/pages/ErrorPage.tsx index 1b1b700a9f..88323282cb 100644 --- a/apps/nextjs-app/src/features/system/pages/ErrorPage.tsx +++ b/apps/nextjs-app/src/features/system/pages/ErrorPage.tsx @@ -1,5 +1,7 @@ -import Head from 'next/head'; +import { useTranslation } from 'next-i18next'; import type { FC } from 'react'; +import { systemConfig } from '@/features/i18n/system.config'; +import { IllustrationPage } from './IllustrationPage'; type Props = { statusCode?: number | null; @@ -11,26 +13,36 @@ type Props = { export const ErrorPage: FC = (props) => { const { error, errorId, message, statusCode } = props; + const { t } = useTranslation(systemConfig.i18nNamespaces); return ( - <> - - Error {statusCode} - -
-
-

Woops !

-

- Something went wrong. Please try again later. -

+
+ +
+
+ Code: + {statusCode}
-
-

Code: {statusCode}

-

Message: {message}

-

Error id: {errorId}

-

ErrorMessage: {error?.message}

+
+ Message: + {message} +
+
+ Error id: + {errorId} +
+
+ ErrorMessage: + {error?.message}
- +
); }; diff --git a/apps/nextjs-app/src/features/system/pages/ForbiddenPage.tsx b/apps/nextjs-app/src/features/system/pages/ForbiddenPage.tsx index 78140a9fb5..afb8653328 100644 --- a/apps/nextjs-app/src/features/system/pages/ForbiddenPage.tsx +++ b/apps/nextjs-app/src/features/system/pages/ForbiddenPage.tsx @@ -1,32 +1,26 @@ -import { Button } from '@teable/ui-lib/shadcn'; -import Head from 'next/head'; import { useTranslation } from 'next-i18next'; import type { FC } from 'react'; import { systemConfig } from '@/features/i18n/system.config'; +import type { IButtonConfig } from './IllustrationPage'; +import { IllustrationPage } from './IllustrationPage'; -type Props = { +type ForbiddenPageProps = { title?: string; - children?: never; + description?: string; + button?: IButtonConfig; }; -export const ForbiddenPage: FC = (props) => { +export const ForbiddenPage: FC = ({ title, description, button }) => { const { t } = useTranslation(systemConfig.i18nNamespaces); - const title = props.title ?? t('system:forbidden.title'); return ( - <> - - {title} - -
-

- {title} -

-

{t('system:forbidden.description')}

- -
- + ); }; diff --git a/apps/nextjs-app/src/features/system/pages/IllustrationPage.tsx b/apps/nextjs-app/src/features/system/pages/IllustrationPage.tsx new file mode 100644 index 0000000000..6f1d53b172 --- /dev/null +++ b/apps/nextjs-app/src/features/system/pages/IllustrationPage.tsx @@ -0,0 +1,63 @@ +import { useTheme } from '@teable/next-themes'; +import { Button } from '@teable/ui-lib/shadcn'; +import Head from 'next/head'; +import Image from 'next/image'; +import type { FC } from 'react'; + +export interface IButtonConfig { + label: string; + href: string; + variant?: 'default' | 'secondary' | 'outline' | 'ghost' | 'link' | 'destructive'; +} + +export interface IIllustrationPageProps { + /** Light theme image path */ + imageLightSrc: string; + /** Dark theme image path */ + imageDarkSrc: string; + /** Image alt text */ + imageAlt?: string; + /** Page title (also used for document title) */ + title: string; + /** Page description */ + description?: string; + /** Button config */ + button: IButtonConfig; +} + +export const IllustrationPage: FC = ({ + imageLightSrc, + imageDarkSrc, + imageAlt = 'Illustration', + title, + description, + button, +}) => { + const { resolvedTheme } = useTheme(); + + const imageSrc = resolvedTheme === 'dark' ? imageDarkSrc : imageLightSrc; + + return ( + <> + + {title} + +
+ {imageAlt} +
+

+ {title} +

+ {description && ( +

+ {description} +

+ )} +
+ +
+ + ); +}; diff --git a/apps/nextjs-app/src/features/system/pages/NotFoundPage.tsx b/apps/nextjs-app/src/features/system/pages/NotFoundPage.tsx index cfefb553cd..666f3ac6ae 100644 --- a/apps/nextjs-app/src/features/system/pages/NotFoundPage.tsx +++ b/apps/nextjs-app/src/features/system/pages/NotFoundPage.tsx @@ -1,32 +1,26 @@ -import { Button } from '@teable/ui-lib/shadcn'; -import Head from 'next/head'; import { useTranslation } from 'next-i18next'; import type { FC } from 'react'; - import { systemConfig } from '@/features/i18n/system.config'; +import type { IButtonConfig } from './IllustrationPage'; +import { IllustrationPage } from './IllustrationPage'; -type Props = { +type NotFoundPageProps = { title?: string; - children?: never; + description?: string; + button?: IButtonConfig; }; -export const NotFoundPage: FC = (props) => { +export const NotFoundPage: FC = ({ title, description, button }) => { const { t } = useTranslation(systemConfig.i18nNamespaces); - const title = props.title ?? t('system:notFound.title'); return ( - <> - - {title} - -
-

- {title} -

- -
- + ); }; diff --git a/apps/nextjs-app/src/features/system/pages/PaymentRequired.tsx b/apps/nextjs-app/src/features/system/pages/PaymentRequired.tsx index f5cb803827..545c307922 100644 --- a/apps/nextjs-app/src/features/system/pages/PaymentRequired.tsx +++ b/apps/nextjs-app/src/features/system/pages/PaymentRequired.tsx @@ -1,34 +1,30 @@ -import { Button } from '@teable/ui-lib/shadcn'; -import Head from 'next/head'; -import { Trans, useTranslation } from 'next-i18next'; +import { useTranslation } from 'next-i18next'; import type { FC } from 'react'; import { systemConfig } from '@/features/i18n/system.config'; +import type { IButtonConfig } from './IllustrationPage'; +import { IllustrationPage } from './IllustrationPage'; -type Props = { +type PaymentRequiredPageProps = { title?: string; - children?: never; + description?: string; + button?: IButtonConfig; }; -export const PaymentRequiredPage: FC = (props) => { +export const PaymentRequiredPage: FC = ({ + title, + description, + button, +}) => { const { t } = useTranslation(systemConfig.i18nNamespaces); - const title = props.title ?? t('system:paymentRequired.title'); return ( - <> - - {title} - -
-

- {title} -

-

- }} /> -

- -
- + ); }; diff --git a/apps/nextjs-app/src/features/system/pages/index.ts b/apps/nextjs-app/src/features/system/pages/index.ts index 5ec79ffb4e..fdc14142a8 100644 --- a/apps/nextjs-app/src/features/system/pages/index.ts +++ b/apps/nextjs-app/src/features/system/pages/index.ts @@ -1,3 +1,5 @@ +export { IllustrationPage } from './IllustrationPage'; +export type { IButtonConfig, IIllustrationPageProps } from './IllustrationPage'; export { NotFoundPage } from './NotFoundPage'; export { ErrorPage } from './ErrorPage'; export { ForbiddenPage } from './ForbiddenPage'; diff --git a/apps/nextjs-app/src/pages/_error.tsx b/apps/nextjs-app/src/pages/_error.tsx index e225e33cde..950d69686b 100644 --- a/apps/nextjs-app/src/pages/_error.tsx +++ b/apps/nextjs-app/src/pages/_error.tsx @@ -7,7 +7,9 @@ import { captureException as sentryCaptureException, flush as sentryFlush } from import type { NextPage, NextPageContext } from 'next'; import NextErrorComponent from 'next/error'; import type { ErrorProps } from 'next/error'; +import { systemConfig } from '@/features/i18n/system.config'; import { ErrorPage } from '@/features/system/pages'; +import { getServerSideTranslations } from '@/lib/i18n'; const sentryIgnoredStatusCodes: number[] = [404, 410]; @@ -78,12 +80,19 @@ const CustomError: NextPage = (props) => { ); }; -CustomError.getInitialProps = async ({ res, err, asPath }: AugmentedNextPageContext) => { +CustomError.getInitialProps = async (context: AugmentedNextPageContext) => { + const { res, err, asPath } = context; + const { locale = 'en' } = context; + const errorInitialProps = (await NextErrorComponent.getInitialProps({ res, err, } as NextPageContext)) as CustomErrorProps; + // Load i18n translations for the error page + const inlinedTranslation = await getServerSideTranslations(locale, systemConfig.i18nNamespaces); + Object.assign(errorInitialProps, inlinedTranslation); + // Workaround for https://github.com/vercel/next.js/issues/8592, mark when // getInitialProps has run errorInitialProps.hasGetInitialPropsRun = true; diff --git a/packages/common-i18n/src/locales/de/common.json b/packages/common-i18n/src/locales/de/common.json index 6c40ce2f88..fce89d391b 100644 --- a/packages/common-i18n/src/locales/de/common.json +++ b/packages/common-i18n/src/locales/de/common.json @@ -6,6 +6,7 @@ "doNotSave": "Nicht speichern", "submit": "Abschicken", "confirm": "Bestätigen", + "continue": "Fortfahren", "close": "Schließen", "edit": "Bearbeiten", "fill": "Ausfüllen", @@ -30,8 +31,11 @@ "yesDelete": "Ja, lösche", "rename": "Umbenennen", "duplicate": "Duplizieren", + "export": "Exportieren", + "import": "Importieren", "change": "Ändern", "upgrade": "Upgrade", + "upgradeToLevel": "Upgrade auf {{level}}", "search": "Suche", "loadMore": "Mehr laden", "collapseSidebar": "Seitenleiste einklappen", @@ -43,15 +47,22 @@ "showAllRow": "Zeige alle Reihen", "hideNotMatchRow": "Nicht übereinstimmende Zeile ausblenden", "more": "Mehr", + "expand": "Erweitern", + "view": "Ansehen", + "preview": "Vorschau", + "viewAndEdit": "Ansehen und bearbeiten", + "deleteTip": "Möchten Sie \"{{name}}\" wirklich löschen?", "move": "Verschieben nach", "turnOn": "Einschalten", "exit": "Ausloggen", "next": "Nächste", "previous": "Vorherige", "select": "Auswählen", - "view": "Ansehen", - "preview": "Vorschau", - "viewAndEdit": "Ansehen und bearbeiten" + "refresh": "Aktualisieren", + "login": "Anmelden", + "useTemplate": "Vorlage verwenden", + "backToSpace": "Zurück zum Space", + "switchBase": "Base wechseln" }, "quickAction": { "title": "Schnelle Aktionen...", @@ -60,12 +71,13 @@ "password": { "setInvalid": "Das Passwort ist ungültig, besteht aus mindestens 8 Zeichen und muss mindestens einen Buchstaben und eine Zahl enthalten." }, - "non": { - "share": "Teilen", - "copy": "Kopiert" - }, "template": { + "non": { + "share": "Teilen", + "copy": "Kopiert" + }, "aiTitle": "Lass uns zusammen bauen", + "aiGreeting": "Wie kann ich Ihnen helfen, {{name}}", "aiSubTitle": "Die erste KI-Plattform, auf der Teams gemeinsam an Daten arbeiten und Produktions-Apps erstellen", "guideTitle": "Beginnen Sie mit Ihrem Szenario", "watchVideo": "Video ansehen", @@ -88,10 +100,14 @@ "guide6": "Erstellen Sie einen Content-Planer mit Beiträgen und Veröffentlichungsdaten", "guide7": "Lebensläufe einfügen → Teable bitten, Kandidaten zu organisieren und vorzusortieren" } + }, + "useTemplateDialog": { + "title": "Space auswählen", + "description": "Bitte wählen Sie einen Space für die Vorlage aus" } }, "settings": { - "title": "Einstellungen", + "title": "Instanzeinstellungen", "personal": { "title": "Persönliche Einstellungen" }, @@ -134,6 +150,20 @@ "addPasswordSuccess": { "title": "🎉 Passwort erfolgreich hinzugefügt." }, + "deleteAccount": { + "title": "Konto löschen", + "desc": "Diese Aktion ist unwiderruflich. Es wird Ihr Konto und alle zugehörigen Daten dauerhaft löschen.", + "error": { + "title": "Konto kann nicht gelöscht werden", + "desc": "Sie müssen zuerst die folgenden Abhängigkeiten behandeln:", + "spacesError": "Bevor Sie Ihr Konto löschen, müssen Sie zuerst Ihre Spaces verlassen (oder löschen und dann in den Papierkorb verschieben)." + }, + "confirm": { + "title": "Bitte geben Sie DELETE zur Bestätigung ein", + "placeholder": "DELETE" + }, + "loading": "Lösche..." + }, "changeEmail": { "title": "E-Mail-Adresse ändern", "desc": "Bitte verifizieren Sie Ihr Passwort und bestätigen Sie Ihre neue E-Mail-Adresse", @@ -161,9 +191,9 @@ "desc": "Sie erhalten E-Mails, wenn Sie Kommentare, Erwähnungen, Seiteneinladungen, Erinnerungen, Zugriffsanfragen und Eigentumsänderungen erhalten." }, "setting": { - "title": "Meine Einstellungen", - "theme": "Theme", - "themeDesc": "Wählen Sie das Theme für die App.", + "title": "Einstellungen", + "theme": "Benutzeroberflächen-Theme", + "themeDesc": "Wählen Sie Ihr Farbschema für die Benutzeroberfläche", "dark": "Dunkel", "light": "Hell", "system": "System", @@ -181,16 +211,65 @@ }, "integration": { "title": "Integrationen", - "description": "Sie haben {{count}} Anwendungen Zugriff auf Ihr Konto gewährt.", - "lastUsed": "Zuletzt verwendet am {{date}}", - "revoke": "Widerrufen", - "owner": "Im Besitz von {{user}}", - "revokeTitle": "Sind Sie sicher, dass Sie die Genehmigung widerrufen wollen?", - "revokeDesc": "{{name}} wird nicht mehr auf die Teable-API zugreifen können. Sie können diese Aktion nicht rückgängig machen.", - "scopeTitle": "Berechtigungen", - "scopeDesc": "Diese Anwendung wird die folgenden Bereiche abrufen können:" + "thirdPartyIntegrations": { + "title": "Drittanbieter-Integrationen", + "description": "Sie haben {{count}} Anwendungen Zugriff auf Ihr Konto gewährt.", + "lastUsed": "Zuletzt verwendet am {{date}}", + "revoke": "Widerrufen", + "owner": "Im Besitz von {{user}}", + "revokeTitle": "Sind Sie sicher, dass Sie die Genehmigung widerrufen wollen?", + "revokeDesc": "{{name}} wird nicht mehr auf die Teable-API zugreifen können. Sie können diese Aktion nicht rückgängig machen.", + "scopeTitle": "Berechtigungen", + "scopeDesc": "Diese Anwendung wird die folgenden Bereiche abrufen können:" + }, + "userIntegration": { + "title": "Verbundene Konten", + "description": "Verbinden Sie externe Konten, um Teable den Zugriff auf Ihre Ressourcen zu ermöglichen.", + "emptyDescription": "Keine verbundenen Konten", + "actions": { + "reconnect": "Erneut verbinden" + }, + "slack": { + "user": "Slack-Benutzer", + "workspace": "Slack-Arbeitsbereich" + }, + "deleteTitle": "Verbundenes Konto entfernen", + "deleteDesc": "Möchten Sie {{name}} wirklich entfernen?", + "create": "Neues Konto verbinden", + "manage": "Verbundene Konten verwalten", + "searchPlaceholder": "Verbundene Konten suchen", + "defaultName": "{{name}} Integration", + "callback": { + "error": "Autorisierung fehlgeschlagen", + "title": "Autorisierung erfolgreich", + "desc": "Sie können dieses Fenster jetzt schließen." + } + } }, "templateAdmin": { + "title": "Vorlagenverwaltung", + "noData": "Keine Daten", + "importing": "Importiere...", + "usageCount": "Nutzungsanzahl: {{count}}", + "useTemplate": "Diese Vorlage verwenden", + "createdBy": "von {{user}}", + "backToTemplateList": "Zurück zur Vorlagenliste", + "tips": { + "errorCategoryName": "Kategorie existiert nicht oder wurde gelöscht", + "needSnapshot": "Bitte erstellen Sie einen Snapshot vor der Veröffentlichung, und der Vorlagenname und die Beschreibung dürfen nicht leer sein", + "needPublish": "Bitte veröffentlichen Sie die Vorlage, bevor Sie sie hervorheben", + "needBaseSource": "Bitte wählen Sie eine Base-Quelle, bevor Sie einen Snapshot erstellen", + "forbiddenUpdateSystemTemplate": "Systemvorlagen können nicht geändert werden", + "addCategoryTips": "Bitte geben Sie zuerst einen Kategorienamen in das Suchfeld ein." + }, + "category": { + "menu": { + "getStarted": "Erste Schritte", + "recommended": "Empfohlen", + "all": "Alle", + "browseByCategory": "Nach Kategorie durchsuchen" + } + }, "header": { "cover": "Umschlag", "name": "Name", @@ -206,7 +285,32 @@ "featured": "Hervorgehoben", "createdBy": "Erstellt von", "userNonExistent": "Benutzer existiert nicht", + "preview": "Vorschau", "usage": "Nutzung" + }, + "actions": { + "title": "Aktionen", + "publish": "Veröffentlichen", + "delete": "Löschen", + "duplicate": "Duplizieren", + "preview": "Vorschau", + "use": "Verwenden", + "pinTop": "Oben anheften", + "addCategory": "Kategorie hinzufügen", + "selectCategory": "Kategorie auswählen", + "viewTemplate": "Vorlage anzeigen" + }, + "relatedTemplates": "Verwandte Vorlagen", + "noImage": "Kein Bild", + "baseSelectPanel": { + "title": "Vorlagenquelle auswählen", + "description": "Wählen Sie eine Base als Vorlage", + "confirm": "Bestätigen", + "search": "Suchen...", + "cancel": "Abbrechen", + "selectBase": "Base auswählen", + "createTemplate": "Vorlage erstellen", + "abnormalBase": "Base existiert nicht oder wurde gelöscht" } } }, @@ -240,15 +344,19 @@ "folder": "Ordner", "newAutomation": "Neue Automatisierung", "newApp": "Neue App", - "newFolder": "Neuer Ordner" + "newFolder": "Neuer Ordner", + "template": "Vorlage" }, "level": { "free": "Free", "plus": "Plus", "pro": "Pro", + "business": "Business", "enterprise": "Enterprise" }, "noResult": "Kein Ergebnis.", + "allNodes": "Alle Knoten", + "noDescription": "Keine Beschreibung", "untitled": "Ohne Titel", "name": "Name", "description": "Beschreibung", @@ -313,6 +421,8 @@ }, "help": { "title": "Hilfe", + "appLink": "https://app.teable.ai", + "mainLink": "https://help.teable.ai", "apiLink": "https://help.teable.ai/en/api-doc/token" }, "pagePermissionChangeTip": "Die Berechtigungen für die Seite wurden aktualisiert. Bitte aktualisieren Sie die Seite, um den neuesten Inhalt zu sehen.", @@ -324,6 +434,10 @@ "unavailableInPlanTips": "Der aktuelle Abonnementplan unterstützt diese Funktion nicht", "unavailableConnectionTips": "Die Datenbankverbindungsfunktion wird in Zukunft entfernt und ist nur in der Enterprise-Edition von öffentlicher Cloud und selbstgehosteten Versionen verfügbar.", "levelTips": "Dieser Space befindet sich derzeit auf dem {{level}} Plan", + "enterpriseFeature": "Enterprise-Funktion", + "automationRequiresUpgrade": "Upgrade auf Enterprise Edition (EE) um Automatisierung zu aktivieren", + "authorityMatrixRequiresUpgrade": "Upgrade auf Enterprise Edition (EE) um Berechtigungsmatrix zu aktivieren", + "viewPricing": "Preise anzeigen", "billable": "Abrechenbar", "billableByAuthorityMatrix": "Abrechnung durch Berechtigungsmatrix generiert", "licenseExpiredGracePeriod": "Ihre Self-Hosted-Lizenz ist abgelaufen und wird am {{expiredTime}} auf den kostenlosen Plan herabgestuft. Bitte aktualisieren Sie Ihre Lizenz umgehend, um Zugriff auf Premium-Funktionen zu behalten.", @@ -345,6 +459,7 @@ }, "admin": { "setting": { + "instanceTitle": "Instanzeinstellungen", "description": "Ändern Sie die Einstellungen für Ihre aktuelle Instanz", "allowSignUp": "Erstellen neuer Konten zulassen", "allowSignUpDescription": "Wenn Sie diese Option deaktivieren, werden neue Benutzerregistrierungen verhindert, und die Schaltfläche „Registrieren“ wird nicht mehr auf der Anmeldeseite angezeigt.", @@ -361,6 +476,7 @@ "brandingSettings": { "title": "Branding-Einstellungen", "description": "Nur in der Enterprise Edition verfügbar", + "brandName": "Markenname", "logo": "Logo", "logoDescription": "Das Logo ist Ihre Markenidentität in Teable.", "logoUpload": "Logo hochladen", diff --git a/packages/common-i18n/src/locales/de/sdk.json b/packages/common-i18n/src/locales/de/sdk.json index 4e8267913f..7af11ffc2b 100644 --- a/packages/common-i18n/src/locales/de/sdk.json +++ b/packages/common-i18n/src/locales/de/sdk.json @@ -1100,7 +1100,8 @@ "deniedByEnabledAuthorityMatrix": "Berechtigung durch aktivierte Berechtigungsmatrix verweigert", "invalidRequestPath": "Anfragepfad ist nicht gültig", "notAllowedOperation": "Sie haben keine Berechtigung, diese Operation auszuführen", - "notAllowedDepartment": "Sie haben keine Berechtigung, auf diese Abteilung zuzugreifen" + "notAllowedDepartment": "Sie haben keine Berechtigung, auf diese Abteilung zuzugreifen", + "templateHeaderInvalid": "Vorlagen-Header ist ungültig" }, "authorityMatrix": { "defaultRoleNotFound": "Standardrolle nicht gefunden", @@ -1149,6 +1150,7 @@ "lookupFieldIdInvalid": "Nachschlagefeld {{lookupFieldId}} ungültig", "formulaExpressionParseError": "Formelausdrucks-Parsefehler", "formulaReferenceNotFound": "Formelreferenzfeld {{fieldIds}} nicht gefunden", + "formulaReferenceNotFieldId": "Formelreferenzen {{fieldIds}} nicht gefunden. Formeln müssen Feld-IDs (fldXXXXXXXXXXXXXXXX Format) verwenden, nicht Feldnamen.", "rollupExpressionParseError": "Rollup-Ausdrucks-Parsefehler", "choiceNameAlreadyExists": "Auswahlname {{name}} existiert bereits", "symmetricFieldIdRequired": "Symmetrische Feld-ID ist erforderlich", @@ -1175,10 +1177,13 @@ }, "view": { "notFound": "Ansicht nicht gefunden", + "cannotDeleteLastView": "Die letzte Ansicht in einer Tabelle kann nicht gelöscht werden. Eine Tabelle muss mindestens eine Ansicht haben.", "defaultViewNotFound": "Standardansicht nicht gefunden", "propertyParseError": "Fehler beim Parsen der Ansichtseigenschaft", "primaryFieldCannotBeHidden": "Primärfeld kann nicht ausgeblendet werden", "filterUnsupportedFieldType": "Filter unterstützt Feldtyp nicht", + "filterInvalidOperator": "Filter hat ungültigen Operator für diesen Feldtyp", + "filterInvalidOperatorMode": "Filter hat ungültige Operator- und Modus-Kombination", "sortUnsupportedFieldType": "Sortierung unterstützt Feldtyp nicht", "groupUnsupportedFieldType": "Gruppierung unterstützt Feldtyp nicht", "anchorNotFound": "Anker-Ansicht nicht gefunden", @@ -1283,7 +1288,8 @@ "domainVerification": { "notFound": "Kein Domain-Verifizierungscode gefunden", "invalidCode": "Ungültiger Verifizierungscode", - "resendCooldown": "Bitte warten Sie 1 Minute bevor Sie einen neuen Code anfordern" + "resendCooldown": "Bitte warten Sie 1 Minute bevor Sie einen neuen Code anfordern", + "alreadyVerified": "Domain bereits verifiziert" }, "organization": { "notFound": "Organisation nicht gefunden", @@ -1354,6 +1360,28 @@ "cannotDeployAppBeforeInitialization": "App kann vor der Initialisierung nicht bereitgestellt werden", "noProjectOrVersionFound": "Kein Projekt oder Version gefunden", "noDeploymentUrlAvailable": "Keine Bereitstellungs-URL verfügbar" + }, + "reward": { + "notFound": "Belohnung nicht gefunden", + "unsupportedSourceType": "Nicht unterstützter Belohnungsquellentyp", + "maxClaimsReached": "Sie haben die maximale Anzahl an Belohnungsansprüchen (2) für diese Woche erreicht", + "verificationFailed": "Verifizierung fehlgeschlagen: {{errors}}", + "alreadyClaimedThisWeek": "Sie haben bereits eine Belohnung für dieses Konto in dieser Woche beansprucht", + "invalidPostUrl": "Ungültiges Beitrags-URL-Format", + "postAlreadyUsed": "Dieser Beitrag wurde bereits verwendet, um eine Belohnung zu beanspruchen", + "unsupportedPlatformUrl": "Nicht unterstützte Social-Media-Plattform-URL", + "unsupportedPlatform": "Nicht unterstützte Plattform: {{platform}}", + "minCharCount": "Beitrag muss mindestens {{count}} Zeichen haben", + "minFollowerCount": "Konto muss mindestens {{count}} Follower haben", + "mustMention": "Beitrag muss {{mention}} erwähnen", + "fetchTweetFailed": "Abrufen des X-Tweets fehlgeschlagen: {{error}}", + "tweetNotFound": "X-Tweet nicht gefunden: {{postId}}", + "fetchUserFailed": "Abrufen des X-Benutzers fehlgeschlagen: {{error}}", + "xUserNotFound": "X-Benutzer nicht gefunden: {{username}}", + "fetchLinkedInPostFailed": "Abrufen des LinkedIn-Beitrags fehlgeschlagen: {{error}}", + "linkedInPostNotFound": "LinkedIn-Beitrag nicht gefunden: {{postId}}", + "linkedInAuthorNotFound": "LinkedIn-Autor nicht gefunden: {{postId}}", + "fetchLinkedInUserFailed": "Abrufen des LinkedIn-Benutzers fehlgeschlagen: {{error}}" } } } diff --git a/packages/common-i18n/src/locales/de/space.json b/packages/common-i18n/src/locales/de/space.json index ed65a7c5f5..187a865f26 100644 --- a/packages/common-i18n/src/locales/de/space.json +++ b/packages/common-i18n/src/locales/de/space.json @@ -41,6 +41,10 @@ "pin": "Pin", "empty": "Ihre gepinnten Bases und Spaces werden hier erscheinen" }, + "tooltip": { + "noPermissionToCreateBase": "Sie haben keine Berechtigung, eine Base zu erstellen", + "creatingBase": "Base wird erstellt..." + }, "tip": { "delete": "Sind Sie sicher, dass Sie <0/> löschen wollen?", "title": "Tipps", diff --git a/packages/common-i18n/src/locales/de/system.json b/packages/common-i18n/src/locales/de/system.json index 9793e9523f..6724c32cb3 100644 --- a/packages/common-i18n/src/locales/de/system.json +++ b/packages/common-i18n/src/locales/de/system.json @@ -1,16 +1,21 @@ { "notFound": { - "title": "404 - Seite wurde nicht gefunden" + "title": "Seite nicht gefunden", + "description": "Der Link, dem Sie gefolgt sind, ist möglicherweise fehlerhaft oder die Seite wurde verschoben." }, "links": { - "backToHome": "Zurück zum Anfang" + "backToHome": "Zurück zur Startseite" }, "forbidden": { - "title": "403 - Verboten", - "description": "Sie haben keine Berechtigung, auf diese Seite zuzugreifen." + "title": "Zugriff eingeschränkt", + "description": "Sie benötigen eine Berechtigung, um auf diese Ressource zuzugreifen.\nBitte wenden Sie sich an Ihren Administrator." }, "paymentRequired": { - "title": "402 - Upgrade des Abonnements erforderlich", - "description": "Your current subscription does not support access to this feature.

Please upgrade your subscription to continue." + "title": "Premium-Funktion freischalten", + "description": "Diese Funktion ist in erweiterten Plänen verfügbar.\nUpgraden Sie, um Ihre Möglichkeiten zu erweitern." + }, + "error": { + "title": "Etwas ist schief gelaufen", + "description": "Ein unerwarteter Fehler ist aufgetreten. Bitte versuchen Sie es später erneut." } } diff --git a/packages/common-i18n/src/locales/en/common.json b/packages/common-i18n/src/locales/en/common.json index 5988932cca..d239e49cb9 100644 --- a/packages/common-i18n/src/locales/en/common.json +++ b/packages/common-i18n/src/locales/en/common.json @@ -35,6 +35,7 @@ "import": "Import", "change": "Change", "upgrade": "Upgrade", + "upgradeToLevel": "Upgrade to {{level}}", "search": "Search", "loadMore": "Load more", "collapseSidebar": "Collapse sidebar", @@ -59,7 +60,9 @@ "select": "Select", "refresh": "Refresh", "login": "Login", - "useTemplate": "Use template" + "useTemplate": "Use template", + "backToSpace": "Back to space", + "switchBase": "Switch base" }, "quickAction": { "title": "Quick actions...", @@ -951,6 +954,16 @@ "unknown": "AI task for table \"{{tableName}}\" was cancelled. Error: {{errorMessage}}" } } + }, + "rewardRejected": { + "title": "Reward Claim Rejected", + "message": "Your {{spaceName}} reward claim has been rejected for the following reason(s): {{errorMessages}}, Please review and submit again.", + "buttonText": "View Space" + }, + "rewardApproved": { + "title": "Reward Claim Approved", + "message": "Congratulations! Your {{spaceName}} reward claim has been approved. {{amount}} credits have been added, valid for {{expiredDays}} days.", + "buttonText": "View Space" } } } @@ -988,5 +1001,103 @@ "description": "Configure the v0 API Key to enable App builder capabilities. Access v0 setting to obtain your API Key", "previewAppError": "App running error", "sendErrorToAI": "Send error to AI" + }, + "credit": { + "title": "Credit", + "leftAmount": "Credits left", + "winFreeCredits": "Win free credits", + "getCredits": "Get 1000 credits", + "winCredit": { + "title": "Share a positive review to earn", + "freeCredits": "1000 free credits", + "guidelinesTitle": "Sharing checklist", + "tagTeableio": "Tag @teableio", + "minCharacters": "Write 100+ characters review", + "minFollowers": "Have 10+ followers", + "limitPerWeek": "500 credits per post, up to 2 posts weekly (X + LinkedIn)", + "postOnX": "Post on X", + "postOnLinkedIn": "Post on LinkedIn", + "preFilledDraft": "🌟 Pre-filled draft ready for you!", + "claimTitle": "Paste post URL to claim your credits", + "userEmail": "User email", + "postUrlLabel": "Post URL (X or LinkedIn)", + "postUrlPlaceholder": "Paste the link to your post here", + "invalidUrl": "Please enter a valid X or LinkedIn post URL", + "claiming": "Claiming...", + "claimCredits": "Claim credits", + "congratulations": "Congratulations!", + "claimSuccess": "You have claimed 500 credits!", + "verifying": "Verifying your post...", + "verifyingDescription": "We're checking your post content, this usually takes a few seconds", + "verifyFailed": "Verification failed", + "tryAgain": "Try Again" + }, + "error": { + "verificationFailed": "Verification failed" + } + }, + "reward": { + "title": "Reward", + "rewardCredits": "Reward credits", + "minCharCount": "Post must have at least {{count}} characters", + "minFollowerCount": "Account must have at least {{count}} followers", + "mustMention": "Post must mention {{mention}}", + "fetchSnapshotFailed": "Failed to fetch post snapshot", + "alreadyClaimedThisWeek": "You have already claimed a reward for this account this week", + "manage": { + "title": "Reward Management", + "description": "View and manage reward records across all spaces", + "overview": "Overview", + "records": "Records", + "searchSpace": "Search space...", + "searchRecords": "Search postUrl/userId...", + "dateRange": "Select date range", + "from": "From", + "to": "To", + "totalSpaces": "Total: {{count}} spaces", + "totalRecords": "Total: {{count}} records", + "space": "Space", + "allSpaces": "All Spaces", + "user": "User", + "creator": "Creator", + "platform": "Platform", + "allStatuses": "All Statuses", + "allPlatforms": "All Platforms", + "pendingCount": "Pending Count", + "approvedCount": "Approved Count", + "rejectedCount": "Rejected Count", + "approvedAmount": "Approved Amount", + "consumedAmount": "Consumed Amount", + "availableAmount": "Available Amount", + "expiringSoonAmount": "Expiring Soon Amount (7d)", + "amount": "Amount", + "remainingAmount": "Remaining Amount", + "createdTime": "Created Time", + "rewardTime": "Rewarded Time", + "expiredTime": "Expired Time", + "lastModified": "Last Modified", + "viewDetails": "View Details", + "details": "Reward Details", + "basicInfo": "Basic Info", + "amountInfo": "Amount Info", + "timeInfo": "Time Info", + "socialInfo": "Social Info", + "verifyResult": "Verification Result", + "uniqueKey": "Unique Key", + "verify": "Verify", + "valid": "Valid", + "invalid": "Invalid", + "errors": "Errors", + "copied": "{{label}} copied", + "openPost": "Open post", + "noData": "No data", + "page": "Page {{current}} of {{total}}", + "status": { + "label": "Status", + "pending": "Pending", + "approved": "Approved", + "rejected": "Rejected" + } + } } } diff --git a/packages/common-i18n/src/locales/en/sdk.json b/packages/common-i18n/src/locales/en/sdk.json index 1cb2fb9e9d..00d4bc5f53 100644 --- a/packages/common-i18n/src/locales/en/sdk.json +++ b/packages/common-i18n/src/locales/en/sdk.json @@ -1360,6 +1360,28 @@ "cannotDeployAppBeforeInitialization": "Cannot deploy app before initialization", "noProjectOrVersionFound": "No project or version found", "noDeploymentUrlAvailable": "No deployment URL available" + }, + "reward": { + "notFound": "Reward not found", + "unsupportedSourceType": "Unsupported reward source type", + "maxClaimsReached": "You have reached the maximum reward claims (2) for this week", + "verificationFailed": "Verification failed: {{errors}}", + "alreadyClaimedThisWeek": "You have already claimed a reward for this account this week", + "invalidPostUrl": "Invalid post URL format", + "postAlreadyUsed": "This post has already been used to claim a reward", + "unsupportedPlatformUrl": "Unsupported social platform URL", + "unsupportedPlatform": "Unsupported platform: {{platform}}", + "minCharCount": "Post must have at least {{count}} characters", + "minFollowerCount": "Account must have at least {{count}} followers", + "mustMention": "Post must mention {{mention}}", + "fetchTweetFailed": "Failed to fetch X tweet: {{error}}", + "tweetNotFound": "X tweet not found: {{postId}}", + "fetchUserFailed": "Failed to fetch X user: {{error}}", + "xUserNotFound": "X user not found: {{username}}", + "fetchLinkedInPostFailed": "Failed to fetch LinkedIn post: {{error}}", + "linkedInPostNotFound": "LinkedIn post not found: {{postId}}", + "linkedInAuthorNotFound": "LinkedIn author not found: {{postId}}", + "fetchLinkedInUserFailed": "Failed to fetch LinkedIn user: {{error}}" } } } diff --git a/packages/common-i18n/src/locales/en/space.json b/packages/common-i18n/src/locales/en/space.json index d28fcb285c..b9fd24d2d5 100644 --- a/packages/common-i18n/src/locales/en/space.json +++ b/packages/common-i18n/src/locales/en/space.json @@ -45,6 +45,10 @@ "pin": "Pin", "empty": "Your pined bases and space will appear here" }, + "tooltip": { + "noPermissionToCreateBase": "You don't have permission to create base", + "creatingBase": "Creating base..." + }, "tip": { "delete": "Are you sure you want to delete <0/>?", "title": "Tips", diff --git a/packages/common-i18n/src/locales/en/system.json b/packages/common-i18n/src/locales/en/system.json index ed78ed585c..54a91c6f3d 100644 --- a/packages/common-i18n/src/locales/en/system.json +++ b/packages/common-i18n/src/locales/en/system.json @@ -1,16 +1,21 @@ { "notFound": { - "title": "404 - Page not found" + "title": "Page not found", + "description": "The link you followed may be broken, or the page has been moved." }, "links": { "backToHome": "Back to home" }, "forbidden": { - "title": "403 - Forbidden", - "description": "You don't have permission to access this page." + "title": "Access restricted", + "description": "You need permission to access this resource.\nPlease contact your admin." }, "paymentRequired": { - "title": "402 - Subscription upgrade required", - "description": "Your current subscription does not support access to this feature.

Please upgrade your subscription to continue." + "title": "Unlock premium feature", + "description": "This feature is available on advanced plans.\nUpgrade to expand your capabilities." + }, + "error": { + "title": "Something went wrong", + "description": "An unexpected issue occurred. Please try again later." } } diff --git a/packages/common-i18n/src/locales/es/common.json b/packages/common-i18n/src/locales/es/common.json index 080e83ef27..4df1c1e64c 100644 --- a/packages/common-i18n/src/locales/es/common.json +++ b/packages/common-i18n/src/locales/es/common.json @@ -32,6 +32,7 @@ "duplicate": "Duplicar", "change": "Cambiar", "upgrade": "Actualizar", + "upgradeToLevel": "Actualizar a {{level}}", "search": "Buscar", "loadMore": "Cargar más", "collapseSidebar": "Colapsar barra lateral", @@ -51,7 +52,17 @@ "select": "Seleccionar", "view": "Ver", "preview": "Vista previa", - "viewAndEdit": "Ver y editar" + "viewAndEdit": "Ver y editar", + "deleteTip": "¿Está seguro de que desea eliminar \"{{name}}\"?", + "refresh": "Actualizar", + "login": "Iniciar sesión", + "useTemplate": "Usar plantilla", + "backToSpace": "Volver al Espacio", + "switchBase": "Cambiar Base", + "continue": "Continuar", + "export": "Exportar", + "import": "Importar", + "expand": "Expandir" }, "quickAction": { "title": "Acciones Rápidas...", @@ -60,12 +71,13 @@ "password": { "setInvalid": "La contraseña no es válida, debe tener al menos 8 caracteres y contener al menos una letra y un número." }, - "non": { - "share": "Compartir", - "copy": "Copiado" - }, "template": { + "non": { + "share": "Compartir", + "copy": "Copiado" + }, "aiTitle": "Construyamos juntos", + "aiGreeting": "¿En qué puedo ayudarte, {{name}}?", "aiSubTitle": "La primera plataforma de IA donde los equipos colaboran en datos y generan aplicaciones de producción", "guideTitle": "Comienza desde tu escenario", "watchVideo": "Ver video", @@ -76,6 +88,10 @@ "loadMore": "Cargar Más", "allTemplatesLoaded": "Todas las plantillas cargadas", "createTemplate": "Crear Plantilla", + "useTemplateDialog": { + "title": "Seleccionar espacio", + "description": "Por favor seleccione un espacio para la plantilla" + }, "promptBox": { "placeholder": "Construye tu aplicación empresarial con Teable", "start": "Iniciar", @@ -91,7 +107,7 @@ } }, "settings": { - "title": "Configuración", + "title": "Configuración de instancia", "personal": { "title": "Configuración personal" }, @@ -134,6 +150,20 @@ "addPasswordSuccess": { "title": "🎉 Agregue la contraseña con éxito." }, + "deleteAccount": { + "title": "Eliminar cuenta", + "desc": "Esta acción es irreversible. Eliminará permanentemente su cuenta y todos los datos asociados.", + "error": { + "title": "No se puede eliminar la cuenta", + "desc": "Debe manejar las siguientes dependencias primero:", + "spacesError": "Antes de eliminar su cuenta, primero debe salir (o eliminar, luego mover a la papelera) de sus Espacios." + }, + "confirm": { + "title": "Por favor escriba DELETE para confirmar", + "placeholder": "DELETE" + }, + "loading": "Eliminando..." + }, "changeEmail": { "title": "Cambiar dirección de correo", "desc": "Verifica tu contraseña y confirma tu nueva dirección de correo", @@ -162,13 +192,17 @@ }, "setting": { "title": "Mis Ajustes", - "theme": "Tema", - "themeDesc": "Selecciona el tema para la aplicación.", + "theme": "Tema de interfaz", + "themeDesc": "Selecciona tu esquema de colores de interfaz", "dark": "Oscuro", "light": "Claro", "system": "Sistema", "version": "Versión de la aplicación", - "language": "Idioma" + "language": "Idioma", + "interactionMode": "Modo de interacción", + "mouseMode": "Modo de cursor", + "touchMode": "Modo táctil", + "systemMode": "Seguir configuración del sistema" }, "nav": { "settings": "Configuración", @@ -176,13 +210,66 @@ "contactSupport": "Contactar con soporte" }, "integration": { - "title": "Integración", - "revoke": "Revocar", - "revokeTitle": "¿Estás seguro de que quieres revocar la autorización?", - "scopeTitle": "Permisos", - "scopeDesc": "Esta aplicación podrá obtener los siguientes ámbitos:" + "title": "Integraciones", + "thirdPartyIntegrations": { + "title": "Integraciones de terceros", + "description": "Ha otorgado acceso a {{count}} aplicaciones a su cuenta.", + "lastUsed": "Último uso {{date}}", + "revoke": "Revocar", + "owner": "Propiedad de {{user}}", + "revokeTitle": "¿Está seguro de que desea revocar la autorización?", + "revokeDesc": "{{name}} ya no podrá acceder a la API de Teable. No puede deshacer esta acción.", + "scopeTitle": "Permisos", + "scopeDesc": "Esta aplicación podrá obtener los siguientes ámbitos:" + }, + "userIntegration": { + "title": "Cuentas conectadas", + "description": "Conecte cuentas externas para permitir que Teable acceda a sus recursos.", + "emptyDescription": "Sin cuentas conectadas", + "actions": { + "reconnect": "Reconectar" + }, + "slack": { + "user": "Usuario de Slack", + "workspace": "Espacio de trabajo de Slack" + }, + "deleteTitle": "Eliminar cuenta conectada", + "deleteDesc": "¿Está seguro de que desea eliminar {{name}}?", + "create": "Conectar nueva cuenta", + "manage": "Administrar cuentas conectadas", + "searchPlaceholder": "Buscar cuentas conectadas", + "defaultName": "Integración de {{name}}", + "callback": { + "error": "Autorización fallida", + "title": "Autorización exitosa", + "desc": "Puede cerrar esta ventana ahora." + } + } }, "templateAdmin": { + "title": "Administración de plantillas", + "noData": "Sin datos", + "importing": "Importando...", + "usageCount": "Recuento de uso: {{count}}", + "useTemplate": "Usar esta plantilla", + "createdBy": "por {{user}}", + "backToTemplateList": "Volver a la lista de plantillas", + "tips": { + "errorCategoryName": "La categoría no existe o fue eliminada", + "needSnapshot": "Por favor cree una instantánea antes de publicar, y el nombre y descripción de la plantilla no pueden estar vacíos", + "needPublish": "Por favor publique la plantilla antes de destacarla", + "needBaseSource": "Por favor seleccione una fuente Base antes de crear la instantánea", + "forbiddenUpdateSystemTemplate": "Las plantillas del sistema no se pueden modificar", + "addCategoryTips": "Por favor ingrese primero un nombre de categoría en el cuadro de búsqueda." + }, + "category": { + "menu": { + "getStarted": "Comenzar", + "recommended": "Recomendado", + "all": "Todos", + "browseByCategory": "Explorar por categoría" + } + }, "header": { "cover": "Portada", "name": "Nombre", @@ -198,7 +285,32 @@ "featured": "Destacado", "createdBy": "Creado por", "userNonExistent": "Usuario no existe", + "preview": "Vista previa", "usage": "Uso" + }, + "actions": { + "title": "Acciones", + "publish": "Publicar", + "delete": "Eliminar", + "duplicate": "Duplicar", + "preview": "Vista previa", + "use": "Usar", + "pinTop": "Fijar arriba", + "addCategory": "Agregar categoría", + "selectCategory": "Seleccionar categoría", + "viewTemplate": "Ver plantilla" + }, + "relatedTemplates": "Plantillas relacionadas", + "noImage": "Sin imagen", + "baseSelectPanel": { + "title": "Seleccionar fuente de plantilla", + "description": "Seleccione una Base como plantilla", + "confirm": "Confirmar", + "search": "Buscar...", + "cancel": "Cancelar", + "selectBase": "Seleccionar Base", + "createTemplate": "Crear plantilla", + "abnormalBase": "La base no existe o fue eliminada" } } }, @@ -232,20 +344,25 @@ "folder": "Carpeta", "newAutomation": "Nueva automatización", "newApp": "Nueva aplicación", - "newFolder": "Nueva carpeta" + "newFolder": "Nueva carpeta", + "template": "Plantilla" }, "level": { "free": "Gratis", - "plus": "Más", + "plus": "Plus", "pro": "Pro", - "enterprise": "Empresa" + "business": "Business", + "enterprise": "Enterprise" }, "noResult": "Sin resultado.", - "untitled": "Intitulado", + "allNodes": "Todos los nodos", + "noDescription": "Sin descripción", + "untitled": "Sin título", "name": "Nombre", "description": "Descripción", "required": "Requerido", "characters": "caracteres", + "atLeastOne": "Reserve al menos un {{noun}}", "guide": { "prev": "Anterior", "next": "Siguiente", @@ -272,6 +389,9 @@ "poweredBy": "Alimentado por <0> ", "invite": { "dialog": { + "title": "Compartir espacio {{spaceName}}", + "desc_one": "Este espacio tiene {{count}} colaborador. Agregar un colaborador de espacio le da acceso a todas las Bases en este espacio.", + "desc_other": "Este espacio tiene {{count}} colaboradores. Agregar un colaborador de espacio les da acceso a todas las Bases en este espacio.", "tabEmail": "Invitar por correo electrónico", "emailPlaceholder": "Introduce las direcciones de correo electrónico, separadas por Enter", "tabLink": "Invitar por enlace", @@ -280,14 +400,19 @@ "linkSend": "Crear enlace", "spaceTitle": "Colaboradores espaciales", "collaboratorSearchPlaceholder": "Encuentra un colaborador espacial por nombre o correo electrónico", + "collaboratorJoin": "se unió {{joinTime}}", "collaboratorRemove": "Eliminar el colaborador", - "linkTitle": "Invitar enlaces", + "linkTitle": "Enlaces de invitación", + "linkCreatedTime": "creado {{createdTime}}", "linkCopySuccess": "Enlace copiado", "linkRemove": "Eliminar enlace" }, "base": { - "baseTitle": "Colaboradores base", - "collaboratorSearchPlaceholder": "Encuentre un colaborador base por nombre o correo electrónico" + "title": "Compartir {{baseName}}", + "desc_one": "Esta Base se comparte con {{count}} colaborador.", + "desc_other": "Esta Base se comparte con {{count}} colaboradores.", + "baseTitle": "Colaboradores de Base", + "collaboratorSearchPlaceholder": "Encuentre un colaborador de Base por nombre o correo electrónico" }, "addOrgCollaborator": { "title": "Agregar colaborador de organización", @@ -296,6 +421,8 @@ }, "help": { "title": "Ayuda", + "appLink": "https://app.teable.ai", + "mainLink": "https://help.teable.ai", "apiLink": "https://help.teable.ai/en/api-doc/token" }, "pagePermissionChangeTip": "Los permisos de la página se han actualizado. ", @@ -306,6 +433,11 @@ "userLimitExceededDescription": "La instancia actual ha alcanzado el número máximo de usuarios permitidos por tu licencia. Por favor, desactiva algunos usuarios o actualiza la licencia.", "unavailableInPlanTips": "El plan de suscripción actual no admite esta función", "unavailableConnectionTips": "La función de conexión de base de datos se eliminará en el futuro y solo estará disponible en la edición Enterprise y en versiones autohospedadas.", + "levelTips": "Este espacio se encuentra actualmente en el plan {{level}}", + "enterpriseFeature": "Función Enterprise", + "automationRequiresUpgrade": "Actualice a Enterprise Edition (EE) para habilitar la automatización", + "authorityMatrixRequiresUpgrade": "Actualice a Enterprise Edition (EE) para habilitar la matriz de autoridad", + "viewPricing": "Ver precios", "billable": "Facturable", "billableByAuthorityMatrix": "Facturación generada por la matriz de permisos", "licenseExpiredGracePeriod": "Su licencia autohospedada ha caducado y se degradará al plan gratuito el {{expiredTime}}. Actualice su licencia de inmediato para mantener el acceso a las funciones premium.", @@ -327,6 +459,7 @@ }, "admin": { "setting": { + "instanceTitle": "Configuración de instancia", "description": "Cambie la configuración de su instancia actual", "allowSignUp": "Permitir crear nuevas cuentas", "allowSignUpDescription": "Deshabilitar esta opción prohibirá los registros de nuevos usuarios, y el botón de registro ya no aparecerá en la página de inicio de sesión.", @@ -339,10 +472,14 @@ "enableWaitlist": "Habilitar lista de espera", "enableWaitlistDescription": "Habilitar esta opción permitirá que los usuarios se registren solo con un código de invitación.", "generalSettings": "Configuración general", - "aiSettings": "Configuración de AI", + "aiSettings": { + "title": "Configuración de IA", + "description": "Configurar los ajustes de IA para esta instancia" + }, "brandingSettings": { "title": "Configuración de marca", "description": "Solo disponible en la Edición Enterprise", + "brandName": "Nombre de marca", "logo": "Logo", "logoDescription": "El logo es su identidad de marca en Teable.", "logoUpload": "Subir logo", @@ -493,9 +630,12 @@ "notification": { "title": "Notificaciones", "unread": "No leído", - "read": "Leer", + "read": "Leído", + "markAs": "Marcar esta notificación como {{status}}", "markAllAsRead": "Marcar todo como leído", + "noUnread": "No hay notificaciones con estado: {{status}}", "changeSetting": "Cambiar la configuración de notificación de página", + "new": "{{count}} nueva(s)", "showMore": "Mostrar más" }, "role": { diff --git a/packages/common-i18n/src/locales/es/sdk.json b/packages/common-i18n/src/locales/es/sdk.json index ae0fd8a637..d93ef2a2b5 100644 --- a/packages/common-i18n/src/locales/es/sdk.json +++ b/packages/common-i18n/src/locales/es/sdk.json @@ -1366,6 +1366,28 @@ "cannotDeployAppBeforeInitialization": "No se puede implementar la aplicación antes de la inicialización", "noProjectOrVersionFound": "No se encontró proyecto o versión", "noDeploymentUrlAvailable": "No hay URL de implementación disponible" + }, + "reward": { + "notFound": "Recompensa no encontrada", + "unsupportedSourceType": "Tipo de fuente de recompensa no soportado", + "maxClaimsReached": "Ha alcanzado el máximo de reclamaciones de recompensa (2) para esta semana", + "verificationFailed": "Verificación fallida: {{errors}}", + "alreadyClaimedThisWeek": "Ya ha reclamado una recompensa para esta cuenta esta semana", + "invalidPostUrl": "Formato de URL de publicación inválido", + "postAlreadyUsed": "Esta publicación ya ha sido utilizada para reclamar una recompensa", + "unsupportedPlatformUrl": "URL de plataforma social no soportada", + "unsupportedPlatform": "Plataforma no soportada: {{platform}}", + "minCharCount": "La publicación debe tener al menos {{count}} caracteres", + "minFollowerCount": "La cuenta debe tener al menos {{count}} seguidores", + "mustMention": "La publicación debe mencionar {{mention}}", + "fetchTweetFailed": "Error al obtener el tweet de X: {{error}}", + "tweetNotFound": "Tweet de X no encontrado: {{postId}}", + "fetchUserFailed": "Error al obtener el usuario de X: {{error}}", + "xUserNotFound": "Usuario de X no encontrado: {{username}}", + "fetchLinkedInPostFailed": "Error al obtener la publicación de LinkedIn: {{error}}", + "linkedInPostNotFound": "Publicación de LinkedIn no encontrada: {{postId}}", + "linkedInAuthorNotFound": "Autor de LinkedIn no encontrado: {{postId}}", + "fetchLinkedInUserFailed": "Error al obtener el usuario de LinkedIn: {{error}}" } } } diff --git a/packages/common-i18n/src/locales/es/space.json b/packages/common-i18n/src/locales/es/space.json index b41766981a..6024d31750 100644 --- a/packages/common-i18n/src/locales/es/space.json +++ b/packages/common-i18n/src/locales/es/space.json @@ -41,6 +41,10 @@ "pin": "Fijar", "empty": "Tus bases y espacios fijados aparecerán aquí" }, + "tooltip": { + "noPermissionToCreateBase": "No tienes permiso para crear una base", + "creatingBase": "Creando base..." + }, "tip": { "delete": "¿Estás seguro de que quieres eliminar <0/>?", "title": "Consejos", diff --git a/packages/common-i18n/src/locales/es/system.json b/packages/common-i18n/src/locales/es/system.json index 5cea9b9d36..5e63d398e6 100644 --- a/packages/common-i18n/src/locales/es/system.json +++ b/packages/common-i18n/src/locales/es/system.json @@ -1,16 +1,21 @@ { "notFound": { - "title": "404 - Página No Encontrada" + "title": "Página no encontrada", + "description": "El enlace que seguiste puede estar roto o la página ha sido movida." }, "links": { "backToHome": "Volver al inicio" }, "forbidden": { - "title": "403 - Acceso Denegado", - "description": "No tienes permiso para acceder a esta página." + "title": "Acceso restringido", + "description": "Necesitas permiso para acceder a este recurso.\nPor favor, contacta con tu administrador." }, "paymentRequired": { - "title": "402 - Se Requiere Actualizar la Suscripción", - "description": "Tu suscripción actual no permite acceder a esta función.

Por favor, actualiza tu suscripción para continuar." + "title": "Desbloquear función Premium", + "description": "Esta función está disponible en planes avanzados.\nActualiza para ampliar tus capacidades." + }, + "error": { + "title": "Algo salió mal", + "description": "Ocurrió un error inesperado. Por favor, inténtalo de nuevo más tarde." } } diff --git a/packages/common-i18n/src/locales/fr/common.json b/packages/common-i18n/src/locales/fr/common.json index 07e7bba60e..b5f6d18132 100644 --- a/packages/common-i18n/src/locales/fr/common.json +++ b/packages/common-i18n/src/locales/fr/common.json @@ -1,6 +1,7 @@ { "actions": { - "title": "Fonctionner", + "title": "Actions", + "add": "Ajouter", "save": "Sauvegarder", "doNotSave": "Ne pas sauvegarder", "submit": "Soumettre", @@ -15,6 +16,7 @@ "zoomIn": "Zoomer", "zoomOut": "Dézoomer", "back": "Retour", + "remove": "Supprimer", "removeConfig": "Supprimer la configuration", "saveSucceed": "Sauvegarde réussie !", "submitSucceed": "Soumission réussie !", @@ -30,12 +32,19 @@ "duplicate": "Dupliquer", "change": "Changer", "upgrade": "Mettre à niveau", + "upgradeToLevel": "Passer à {{level}}", "search": "Rechercher", "loadMore": "Charger plus", "collapseSidebar": "Réduire la barre latérale", "restore": "Récupérer", "permanentDelete": "Suppression permanente", + "globalSearch": "Recherche globale", + "fieldSearch": "Recherche par champ", + "tableIndex": "Index", + "showAllRow": "Afficher toutes les lignes", + "hideNotMatchRow": "Masquer les lignes non correspondantes", "more": "Plus", + "expand": "Développer", "move": "Déplacer vers", "turnOn": "Activer", "exit": "Déconnexion", @@ -44,7 +53,16 @@ "select": "Sélectionner", "view": "Voir", "preview": "Aperçu", - "viewAndEdit": "Voir et modifier" + "viewAndEdit": "Voir et modifier", + "deleteTip": "Êtes-vous sûr de vouloir supprimer \"{{name}}\" ?", + "refresh": "Actualiser", + "login": "Se connecter", + "useTemplate": "Utiliser le modèle", + "backToSpace": "Retour à l'espace", + "switchBase": "Changer de base", + "continue": "Continuer", + "export": "Exporter", + "import": "Importer" }, "quickAction": { "title": "Actions rapides...", @@ -53,12 +71,13 @@ "password": { "setInvalid": "Le mot de passe n'est pas valide, il doit contenir au moins 8 caractères et au moins une lettre et un chiffre." }, - "non": { - "share": "Partager", - "copy": "Copié" - }, "template": { + "non": { + "share": "Partager", + "copy": "Copié" + }, "aiTitle": "Construisons ensemble", + "aiGreeting": "Comment puis-je vous aider, {{name}} ?", "aiSubTitle": "La première plateforme IA où les équipes collaborent sur les données et génèrent des applications de production", "guideTitle": "Commencez à partir de votre scénario", "watchVideo": "Regarder la vidéo", @@ -69,6 +88,10 @@ "loadMore": "Charger Plus", "allTemplatesLoaded": "Tous les modèles chargés", "createTemplate": "Créer un Modèle", + "useTemplateDialog": { + "title": "Sélectionner un espace", + "description": "Veuillez sélectionner un espace pour le modèle" + }, "promptBox": { "placeholder": "Créez votre application métier avec Teable", "start": "Démarrer", @@ -84,7 +107,7 @@ } }, "settings": { - "title": "Paramètres", + "title": "Paramètres d'instance", "personal": { "title": "Paramètres personnels" }, @@ -129,6 +152,20 @@ "addPasswordSuccess": { "title": "🎉 Mot de passe ajouté avec succès." }, + "deleteAccount": { + "title": "Supprimer le compte", + "desc": "Cette action est irréversible. Elle supprimera définitivement votre compte et toutes les données associées.", + "error": { + "title": "Impossible de supprimer le compte", + "desc": "Vous devez d'abord gérer les dépendances suivantes :", + "spacesError": "Avant de supprimer votre compte, vous devez d'abord quitter (ou supprimer, puis déplacer vers la corbeille) vos Espaces." + }, + "confirm": { + "title": "Veuillez taper DELETE pour confirmer", + "placeholder": "DELETE" + }, + "loading": "Suppression en cours..." + }, "changeEmail": { "title": "Changer l'adresse e-mail", "desc": "Veuillez vérifier votre mot de passe et confirmer votre nouvelle adresse e-mail", @@ -176,16 +213,65 @@ }, "integration": { "title": "Intégrations", - "description": "Vous avez accordé l'accès à {{count}} applications à votre compte.", - "lastUsed": "Dernière utilisation à {{date}}", - "revoke": "Révoquer", - "owner": "Propriétaire par {{user}}", - "revokeTitle": "Êtes-vous sûr de vouloir révoquer l'autorisation ?", - "revokeDesc": "{{name}} ne pourra plus accéder à l'API Teable. Vous ne pouvez pas annuler cette action.", - "scopeTitle": "Autorisations", - "scopeDesc": "Cette application pourra obtenir les autorisations suivantes :" + "thirdPartyIntegrations": { + "title": "Intégrations tierces", + "description": "Vous avez accordé l'accès à {{count}} applications à votre compte.", + "lastUsed": "Dernière utilisation {{date}}", + "revoke": "Révoquer", + "owner": "Propriété de {{user}}", + "revokeTitle": "Êtes-vous sûr de vouloir révoquer l'autorisation ?", + "revokeDesc": "{{name}} ne pourra plus accéder à l'API Teable. Vous ne pouvez pas annuler cette action.", + "scopeTitle": "Autorisations", + "scopeDesc": "Cette application pourra obtenir les autorisations suivantes :" + }, + "userIntegration": { + "title": "Comptes connectés", + "description": "Connectez des comptes externes pour permettre à Teable d'accéder à vos ressources.", + "emptyDescription": "Aucun compte connecté", + "actions": { + "reconnect": "Reconnecter" + }, + "slack": { + "user": "Utilisateur Slack", + "workspace": "Espace de travail Slack" + }, + "deleteTitle": "Supprimer le compte connecté", + "deleteDesc": "Êtes-vous sûr de vouloir supprimer {{name}} ?", + "create": "Connecter un nouveau compte", + "manage": "Gérer les comptes connectés", + "searchPlaceholder": "Rechercher les comptes connectés", + "defaultName": "Intégration {{name}}", + "callback": { + "error": "Échec de l'autorisation", + "title": "Autorisation réussie", + "desc": "Vous pouvez maintenant fermer cette fenêtre." + } + } }, "templateAdmin": { + "title": "Administration des modèles", + "noData": "Aucune donnée", + "importing": "Importation...", + "usageCount": "Nombre d'utilisations: {{count}}", + "useTemplate": "Utiliser ce modèle", + "createdBy": "par {{user}}", + "backToTemplateList": "Retour à la liste des modèles", + "tips": { + "errorCategoryName": "La catégorie n'existe pas ou a été supprimée", + "needSnapshot": "Veuillez créer un instantané avant de publier, et le nom et la description du modèle ne peuvent pas être vides", + "needPublish": "Veuillez publier le modèle avant de le mettre en vedette", + "needBaseSource": "Veuillez sélectionner une source Base avant de créer l'instantané", + "forbiddenUpdateSystemTemplate": "Les modèles système ne peuvent pas être modifiés", + "addCategoryTips": "Veuillez d'abord saisir un nom de catégorie dans la zone de recherche." + }, + "category": { + "menu": { + "getStarted": "Commencer", + "recommended": "Recommandé", + "all": "Tous", + "browseByCategory": "Parcourir par catégorie" + } + }, "header": { "cover": "Couverture", "name": "Nom", @@ -201,7 +287,32 @@ "featured": "En vedette", "createdBy": "Créé par", "userNonExistent": "Utilisateur inexistant", + "preview": "Aperçu", "usage": "Utilisation" + }, + "actions": { + "title": "Actions", + "publish": "Publier", + "delete": "Supprimer", + "duplicate": "Dupliquer", + "preview": "Aperçu", + "use": "Utiliser", + "pinTop": "Épingler en haut", + "addCategory": "Ajouter une catégorie", + "selectCategory": "Sélectionner une catégorie", + "viewTemplate": "Voir le modèle" + }, + "relatedTemplates": "Modèles associés", + "noImage": "Aucune image", + "baseSelectPanel": { + "title": "Sélectionner la source du modèle", + "description": "Sélectionnez une Base comme modèle", + "confirm": "Confirmer", + "search": "Rechercher...", + "cancel": "Annuler", + "selectBase": "Sélectionner une Base", + "createTemplate": "Créer un modèle", + "abnormalBase": "La base n'existe pas ou a été supprimée" } } }, @@ -235,20 +346,25 @@ "folder": "Dossier", "newAutomation": "Nouvelle automatisation", "newApp": "Nouvelle application", - "newFolder": "Nouveau dossier" + "newFolder": "Nouveau dossier", + "template": "Modèle" }, "level": { "free": "Gratuit", "plus": "Plus", "pro": "Pro", - "enterprise": "Entreprise" + "business": "Business", + "enterprise": "Enterprise" }, "noResult": "Aucun résultat.", + "allNodes": "Tous les nœuds", + "noDescription": "Pas de description", "untitled": "Sans titre", "name": "Nom", "description": "Description", "required": "Requis", "characters": "caractères", + "atLeastOne": "Conservez au moins un {{noun}}", "guide": { "prev": "Précédent", "next": "Suivant", @@ -275,7 +391,7 @@ "poweredBy": "Propulsé par <0>", "invite": { "dialog": { - "title": "Partage de l'espace {{spaceName}}", + "title": "Partager l'espace {{spaceName}}", "desc_one": "Cet espace a {{count}} collaborateur. Ajouter un collaborateur à l'espace lui donnera accès à toutes les bases au sein de cet espace.", "desc_other": "Cet espace a {{count}} collaborateurs. Ajouter un collaborateur à l'espace lui donnera accès à toutes les bases au sein de cet espace.", "tabEmail": "Inviter par email", @@ -292,10 +408,23 @@ "linkCreatedTime": "créé {{createdTime}}", "linkCopySuccess": "Lien copié", "linkRemove": "Supprimer le lien" + }, + "base": { + "title": "Partager {{baseName}}", + "desc_one": "Cette Base est partagée avec {{count}} collaborateur.", + "desc_other": "Cette Base est partagée avec {{count}} collaborateurs.", + "baseTitle": "Collaborateurs de la Base", + "collaboratorSearchPlaceholder": "Trouver un collaborateur de Base par nom ou email" + }, + "addOrgCollaborator": { + "title": "Ajouter un collaborateur d'organisation", + "placeholder": "Sélectionner un membre ou département de l'organisation" } }, "help": { "title": "Aide", + "appLink": "https://app.teable.ai", + "mainLink": "https://help.teable.ai", "apiLink": "https://help.teable.ai/en/api-doc/token" }, "pagePermissionChangeTip": "Les autorisations de la page ont été mises à jour. Veuillez rafraîchir la page pour voir le contenu le plus récent.", @@ -307,6 +436,10 @@ "unavailableInPlanTips": "Le plan d'abonnement actuel ne prend pas en charge cette fonctionnalité", "unavailableConnectionTips": "La fonctionnalité de connexion de base de données sera supprimée et ne sera disponible que dans la version Entreprise et les versions auto-hébergées.", "levelTips": "Cet espace est actuellement sur le plan {{level}}", + "enterpriseFeature": "Fonctionnalité Enterprise", + "automationRequiresUpgrade": "Passez à Enterprise Edition (EE) pour activer l'automatisation", + "authorityMatrixRequiresUpgrade": "Passez à Enterprise Edition (EE) pour activer la matrice d'autorité", + "viewPricing": "Voir les tarifs", "billable": "Facturable", "billableByAuthorityMatrix": "Facturation générée par la matrice d'autorités", "licenseExpiredGracePeriod": "Votre licence auto-hébergée a expiré et sera rétrogradée au forfait gratuit le {{expiredTime}}. Veuillez mettre à jour votre licence rapidement pour conserver l'accès aux fonctionnalités premium.", @@ -328,6 +461,7 @@ }, "admin": { "setting": { + "instanceTitle": "Paramètres d'instance", "description": "Modifier les paramètres de votre instance actuelle", "allowSignUp": "Autoriser la création de nouveaux comptes", "allowSignUpDescription": "Désactiver cette option interdira les nouvelles inscriptions d'utilisateur, et le bouton d'inscription n'apparaîtra plus sur la page de connexion.", @@ -340,10 +474,14 @@ "enableWaitlist": "Activer la liste d'attente", "enableWaitlistDescription": "Activer cette option permettra aux utilisateurs de s'inscrire uniquement avec un code d'invitation.", "generalSettings": "Paramètres généraux", - "aiSettings": "Paramètres AI", + "aiSettings": { + "title": "Paramètres IA", + "description": "Configurer les paramètres IA pour cette instance" + }, "brandingSettings": { "title": "Paramètres de marque", "description": "Disponible uniquement dans l'Édition Enterprise", + "brandName": "Nom de marque", "logo": "Logo", "logoDescription": "Le logo est votre identité de marque dans Teable.", "logoUpload": "Télécharger le logo", @@ -502,8 +640,24 @@ "new": "nouveau {{count}}", "showMore": "Voir plus" }, + "role": { + "title": { + "owner": "Propriétaire", + "creator": "Créateur", + "editor": "Éditeur", + "commenter": "Commentateur", + "viewer": "Lecteur" + }, + "description": { + "owner": "Peut entièrement configurer et modifier les bases, automatisations, matrices d'autorité et gérer les paramètres et la facturation de l'espace", + "creator": "Peut entièrement configurer et modifier les bases, automatisations et matrices d'autorité", + "editor": "Peut modifier les enregistrements et les vues, mais ne peut pas configurer les tables ou les champs", + "commenter": "Peut commenter les enregistrements", + "viewer": "Ne peut pas modifier ou commenter" + } + }, "trash": { - "spaceTrash": "Corbeille d'espace", + "spaceTrash": "Corbeille de l'espace", "type": "Type", "resetTrash": "Vider", "deletedBy": "Supprimé par", @@ -514,8 +668,18 @@ "addToTrash": "Déplacer dans la corbeille", "description": "Les données dans la corbeille occupent toujours un espace d'utilisation de records et d'utilisation de pièces jointes." }, + "pluginCenter": { + "pluginUrlEmpty": "Le plugin n'a pas d'URL", + "install": "Installer", + "publisher": "Éditeur", + "lastUpdated": "Dernière mise à jour", + "pluginNotFound": "Plugin introuvable", + "pluginEmpty": { + "title": "Pas encore de plugins" + } + }, "automation": { - "turnOnTip": "Voulez-vous activer l'automatisation actuelle?" + "turnOnTip": "Voulez-vous activer l'automatisation actuelle ?" }, "email": { "send": "Envoyer", diff --git a/packages/common-i18n/src/locales/fr/sdk.json b/packages/common-i18n/src/locales/fr/sdk.json index a016c58a6d..072c55364a 100644 --- a/packages/common-i18n/src/locales/fr/sdk.json +++ b/packages/common-i18n/src/locales/fr/sdk.json @@ -1354,6 +1354,28 @@ "cannotDeployAppBeforeInitialization": "Impossible de déployer l'application avant l'initialisation", "noProjectOrVersionFound": "Aucun projet ou version trouvé", "noDeploymentUrlAvailable": "Aucune URL de déploiement disponible" + }, + "reward": { + "notFound": "Récompense non trouvée", + "unsupportedSourceType": "Type de source de récompense non pris en charge", + "maxClaimsReached": "Vous avez atteint le maximum de réclamations de récompenses (2) pour cette semaine", + "verificationFailed": "Vérification échouée : {{errors}}", + "alreadyClaimedThisWeek": "Vous avez déjà réclamé une récompense pour ce compte cette semaine", + "invalidPostUrl": "Format d'URL de publication invalide", + "postAlreadyUsed": "Cette publication a déjà été utilisée pour réclamer une récompense", + "unsupportedPlatformUrl": "URL de plateforme sociale non prise en charge", + "unsupportedPlatform": "Plateforme non prise en charge : {{platform}}", + "minCharCount": "La publication doit contenir au moins {{count}} caractères", + "minFollowerCount": "Le compte doit avoir au moins {{count}} abonnés", + "mustMention": "La publication doit mentionner {{mention}}", + "fetchTweetFailed": "Échec de la récupération du tweet X : {{error}}", + "tweetNotFound": "Tweet X non trouvé : {{postId}}", + "fetchUserFailed": "Échec de la récupération de l'utilisateur X : {{error}}", + "xUserNotFound": "Utilisateur X non trouvé : {{username}}", + "fetchLinkedInPostFailed": "Échec de la récupération de la publication LinkedIn : {{error}}", + "linkedInPostNotFound": "Publication LinkedIn non trouvée : {{postId}}", + "linkedInAuthorNotFound": "Auteur LinkedIn non trouvé : {{postId}}", + "fetchLinkedInUserFailed": "Échec de la récupération de l'utilisateur LinkedIn : {{error}}" } } } diff --git a/packages/common-i18n/src/locales/fr/space.json b/packages/common-i18n/src/locales/fr/space.json index cee249cee6..80d766ea3e 100644 --- a/packages/common-i18n/src/locales/fr/space.json +++ b/packages/common-i18n/src/locales/fr/space.json @@ -41,6 +41,10 @@ "pin": "Épingler", "empty": "Vos bases et espaces favoris apparaîtront ici" }, + "tooltip": { + "noPermissionToCreateBase": "Vous n'avez pas la permission de créer une base", + "creatingBase": "Création de la base..." + }, "tip": { "delete": "Êtes-vous sûr de vouloir supprimer <0/> ?", "title": "Conseils", diff --git a/packages/common-i18n/src/locales/fr/system.json b/packages/common-i18n/src/locales/fr/system.json index d78f84bbe0..a94fad65d8 100644 --- a/packages/common-i18n/src/locales/fr/system.json +++ b/packages/common-i18n/src/locales/fr/system.json @@ -1,16 +1,21 @@ { "notFound": { - "title": "404 - Page non trouvée" + "title": "Page non trouvée", + "description": "Le lien que vous avez suivi est peut-être rompu ou la page a été déplacée." }, "links": { "backToHome": "Retour à l'accueil" }, "forbidden": { - "title": "403 - Accès interdit", - "description": "Vous n'avez pas la permission d'accéder à cette page." + "title": "Accès restreint", + "description": "Vous avez besoin d'une autorisation pour accéder à cette ressource.\nVeuillez contacter votre administrateur." }, "paymentRequired": { - "title": "402 - Mise à niveau de l'abonnement requise", - "description": "Votre abonnement actuel ne permet pas d'accéder à cette fonctionnalité.

Veuillez mettre à niveau votre abonnement pour continuer." + "title": "Débloquer la fonctionnalité Premium", + "description": "Cette fonctionnalité est disponible dans les forfaits avancés.\nPassez à un forfait supérieur pour étendre vos capacités." + }, + "error": { + "title": "Une erreur s'est produite", + "description": "Une erreur inattendue s'est produite. Veuillez réessayer plus tard." } } diff --git a/packages/common-i18n/src/locales/it/common.json b/packages/common-i18n/src/locales/it/common.json index d1d290f777..a50b111445 100644 --- a/packages/common-i18n/src/locales/it/common.json +++ b/packages/common-i18n/src/locales/it/common.json @@ -32,6 +32,7 @@ "duplicate": "Duplica", "change": "Cambia", "upgrade": "Aggiorna", + "upgradeToLevel": "Aggiorna a {{level}}", "search": "Cerca", "loadMore": "Carica altro", "collapseSidebar": "Comprimi barra laterale", @@ -51,7 +52,17 @@ "select": "Seleziona", "view": "Visualizza", "preview": "Anteprima", - "viewAndEdit": "Visualizza e modifica" + "viewAndEdit": "Visualizza e modifica", + "deleteTip": "Sei sicuro di voler eliminare \"{{name}}\"?", + "refresh": "Aggiorna", + "login": "Accedi", + "useTemplate": "Usa modello", + "backToSpace": "Torna allo Spazio", + "switchBase": "Cambia Base", + "continue": "Continua", + "export": "Esporta", + "import": "Importa", + "expand": "Espandi" }, "quickAction": { "title": "Azioni rapide...", @@ -60,12 +71,13 @@ "password": { "setInvalid": "La password non è valida, deve contenere almeno 8 caratteri e almeno una lettera e un numero." }, - "non": { - "share": "Condividi", - "copy": "Copiato" - }, "template": { + "non": { + "share": "Condividi", + "copy": "Copiato" + }, "aiTitle": "Costruiamo insieme", + "aiGreeting": "Come posso aiutarti, {{name}}?", "aiSubTitle": "La prima piattaforma AI dove i team collaborano sui dati e generano app di produzione", "guideTitle": "Inizia dal tuo scenario", "watchVideo": "Guarda il video", @@ -76,6 +88,10 @@ "loadMore": "Carica Altro", "allTemplatesLoaded": "Tutti i modelli caricati", "createTemplate": "Crea Modello", + "useTemplateDialog": { + "title": "Seleziona spazio", + "description": "Seleziona uno spazio per il modello" + }, "promptBox": { "placeholder": "Costruisci la tua app aziendale con Teable", "start": "Inizia", @@ -91,7 +107,7 @@ } }, "settings": { - "title": "Impostazioni", + "title": "Impostazioni dell'istanza", "personal": { "title": "Impostazioni personali" }, @@ -134,6 +150,20 @@ "addPasswordSuccess": { "title": "🎉 Aggiunta password riuscita." }, + "deleteAccount": { + "title": "Elimina account", + "desc": "Questa azione è irreversibile. Eliminerà permanentemente il tuo account e tutti i dati associati.", + "error": { + "title": "Impossibile eliminare l'account", + "desc": "Devi prima gestire le seguenti dipendenze:", + "spacesError": "Prima di eliminare il tuo account, devi prima uscire (o eliminare, poi spostare nel cestino) dai tuoi Spazi." + }, + "confirm": { + "title": "Digita DELETE per confermare", + "placeholder": "DELETE" + }, + "loading": "Eliminazione in corso..." + }, "changeEmail": { "title": "Modifica indirizzo email", "desc": "Verifica la tua password e conferma il tuo nuovo indirizzo email", @@ -162,13 +192,17 @@ }, "setting": { "title": "Le mie impostazioni", - "theme": "Tema", - "themeDesc": "Seleziona il tema per l'app.", + "theme": "Tema interfaccia", + "themeDesc": "Seleziona lo schema di colori dell'interfaccia", "dark": "Scuro", "light": "Chiaro", "system": "Sistema", "version": "Versione dell'app", - "language": "Lingua" + "language": "Lingua", + "interactionMode": "Modalità di interazione", + "mouseMode": "Modalità cursore", + "touchMode": "Modalità touch", + "systemMode": "Segui le impostazioni di sistema" }, "nav": { "settings": "Impostazioni", @@ -177,14 +211,40 @@ }, "integration": { "title": "Integrazioni", - "description": "Hai concesso l'accesso al tuo account a {{count}} applicazioni.", - "lastUsed": "Ultimo utilizzo il {{date}}", - "revoke": "Revoca", - "owner": "Proprietario di {{user}}", - "revokeTitle": "Sei sicuro di voler revocare l'autorizzazione?", - "revokeDesc": "{{name}} non sarà più in grado di accedere all'API di Teable. Non puoi annullare questa azione.", - "scopeTitle": "Permessi", - "scopeDesc": "Questa applicazione sarà in grado di ottenere i seguenti ambiti:" + "thirdPartyIntegrations": { + "title": "Integrazioni di terze parti", + "description": "Hai concesso l'accesso al tuo account a {{count}} applicazioni.", + "lastUsed": "Ultimo utilizzo {{date}}", + "revoke": "Revoca", + "owner": "Di proprietà di {{user}}", + "revokeTitle": "Sei sicuro di voler revocare l'autorizzazione?", + "revokeDesc": "{{name}} non sarà più in grado di accedere all'API di Teable. Non puoi annullare questa azione.", + "scopeTitle": "Permessi", + "scopeDesc": "Questa applicazione sarà in grado di ottenere i seguenti ambiti:" + }, + "userIntegration": { + "title": "Account connessi", + "description": "Connetti account esterni per consentire a Teable di accedere alle tue risorse.", + "emptyDescription": "Nessun account connesso", + "actions": { + "reconnect": "Riconnetti" + }, + "slack": { + "user": "Utente Slack", + "workspace": "Area di lavoro Slack" + }, + "deleteTitle": "Rimuovi account connesso", + "deleteDesc": "Sei sicuro di voler rimuovere {{name}}?", + "create": "Connetti nuovo account", + "manage": "Gestisci account connessi", + "searchPlaceholder": "Cerca account connessi", + "defaultName": "Integrazione {{name}}", + "callback": { + "error": "Autorizzazione fallita", + "title": "Autorizzazione riuscita", + "desc": "Puoi chiudere questa finestra ora." + } + } }, "templateAdmin": { "header": { @@ -236,15 +296,19 @@ "folder": "Cartella", "newAutomation": "Nuova automazione", "newApp": "Nuova applicazione", - "newFolder": "Nuova cartella" + "newFolder": "Nuova cartella", + "template": "Modello" }, "level": { "free": "Gratuito", "plus": "Plus", "pro": "Pro", + "business": "Business", "enterprise": "Enterprise" }, "noResult": "Nessun risultato.", + "allNodes": "Tutti i nodi", + "noDescription": "Nessuna descrizione", "untitled": "Senza titolo", "name": "Nome", "description": "Descrizione", @@ -309,6 +373,8 @@ }, "help": { "title": "Aiuto", + "appLink": "https://app.teable.ai", + "mainLink": "https://help.teable.ai", "apiLink": "https://help.teable.ai/en/api-doc/token" }, "pagePermissionChangeTip": "I permessi della pagina sono stati aggiornati. Si prega di aggiornare la pagina per vedere il contenuto più recente.", @@ -320,6 +386,10 @@ "unavailableInPlanTips": "Il piano di abbonamento attuale non supporta questa funzione", "unavailableConnectionTips": "La funzione di connessione al database verrà rimossa in futuro e sarà disponibile solo nella versione Enterprise e nelle versioni self-hosted.", "levelTips": "Questo spazio è attualmente sul piano {{level}}", + "enterpriseFeature": "Funzionalità Enterprise", + "automationRequiresUpgrade": "Aggiorna a Enterprise Edition (EE) per abilitare l'automazione", + "authorityMatrixRequiresUpgrade": "Aggiorna a Enterprise Edition (EE) per abilitare la matrice di autorità", + "viewPricing": "Visualizza prezzi", "billable": "Fatturabile", "billableByAuthorityMatrix": "Fatturazione generata dalla matrice dei permessi", "licenseExpiredGracePeriod": "La tua licenza self-hosted è scaduta e verrà declassata al piano gratuito il {{expiredTime}}. Aggiorna la tua licenza tempestivamente per mantenere l'accesso alle funzionalità premium.", @@ -341,6 +411,7 @@ }, "admin": { "setting": { + "instanceTitle": "Impostazioni dell'istanza", "description": "Modifica le impostazioni per la tua istanza corrente", "allowSignUp": "Consenti la creazione di nuovi account", "allowSignUpDescription": "Disabilitando questa opzione si proibiranno le nuove registrazioni degli utenti e il pulsante di registrazione non apparirà più nella pagina di accesso.", @@ -353,10 +424,14 @@ "enableWaitlist": "Abilita la lista d'attesa", "enableWaitlistDescription": "Abilitando questa opzione si consentirà agli utenti di registrarsi solo con un codice di invito.", "generalSettings": "Impostazioni generali", - "aiSettings": "Impostazioni AI", + "aiSettings": { + "title": "Impostazioni AI", + "description": "Configura le impostazioni AI per questa istanza" + }, "brandingSettings": { "title": "Impostazioni di branding", "description": "Disponibile solo nell'Edizione Enterprise", + "brandName": "Nome del marchio", "logo": "Logo", "logoDescription": "Il logo è la tua identità di marca in Teable.", "logoUpload": "Carica logo", diff --git a/packages/common-i18n/src/locales/it/sdk.json b/packages/common-i18n/src/locales/it/sdk.json index 394bcd138d..0c71260666 100644 --- a/packages/common-i18n/src/locales/it/sdk.json +++ b/packages/common-i18n/src/locales/it/sdk.json @@ -1354,6 +1354,28 @@ "cannotDeployAppBeforeInitialization": "Impossibile distribuire l'app prima dell'inizializzazione", "noProjectOrVersionFound": "Nessun progetto o versione trovato", "noDeploymentUrlAvailable": "Nessun URL di distribuzione disponibile" + }, + "reward": { + "notFound": "Premio non trovato", + "unsupportedSourceType": "Tipo di fonte premio non supportato", + "maxClaimsReached": "Hai raggiunto il massimo di richieste premio (2) per questa settimana", + "verificationFailed": "Verifica fallita: {{errors}}", + "alreadyClaimedThisWeek": "Hai già richiesto un premio per questo account questa settimana", + "invalidPostUrl": "Formato URL del post non valido", + "postAlreadyUsed": "Questo post è già stato utilizzato per richiedere un premio", + "unsupportedPlatformUrl": "URL della piattaforma social non supportato", + "unsupportedPlatform": "Piattaforma non supportata: {{platform}}", + "minCharCount": "Il post deve avere almeno {{count}} caratteri", + "minFollowerCount": "L'account deve avere almeno {{count}} follower", + "mustMention": "Il post deve menzionare {{mention}}", + "fetchTweetFailed": "Impossibile recuperare il tweet X: {{error}}", + "tweetNotFound": "Tweet X non trovato: {{postId}}", + "fetchUserFailed": "Impossibile recuperare l'utente X: {{error}}", + "xUserNotFound": "Utente X non trovato: {{username}}", + "fetchLinkedInPostFailed": "Impossibile recuperare il post LinkedIn: {{error}}", + "linkedInPostNotFound": "Post LinkedIn non trovato: {{postId}}", + "linkedInAuthorNotFound": "Autore LinkedIn non trovato: {{postId}}", + "fetchLinkedInUserFailed": "Impossibile recuperare l'utente LinkedIn: {{error}}" } } } diff --git a/packages/common-i18n/src/locales/it/space.json b/packages/common-i18n/src/locales/it/space.json index 5f175eeb69..9be234302d 100644 --- a/packages/common-i18n/src/locales/it/space.json +++ b/packages/common-i18n/src/locales/it/space.json @@ -41,6 +41,10 @@ "pin": "Preferiti", "empty": "Le tue basi e spazi preferiti appariranno qui" }, + "tooltip": { + "noPermissionToCreateBase": "Non hai il permesso di creare una base", + "creatingBase": "Creazione della base in corso..." + }, "tip": { "delete": "Sei sicuro di voler eliminare <0/>?", "title": "Suggerimenti", diff --git a/packages/common-i18n/src/locales/it/system.json b/packages/common-i18n/src/locales/it/system.json index e547ff7a78..4e89ecc1cd 100644 --- a/packages/common-i18n/src/locales/it/system.json +++ b/packages/common-i18n/src/locales/it/system.json @@ -1,16 +1,21 @@ { "notFound": { - "title": "404 - Pagina Non Trovata" + "title": "Pagina non trovata", + "description": "Il link che hai seguito potrebbe essere non valido o la pagina è stata spostata." }, "links": { "backToHome": "Torna alla home" }, "forbidden": { - "title": "403 - Accesso negato", - "description": "Non hai il permesso di accedere a questa pagina." + "title": "Accesso limitato", + "description": "Hai bisogno dell'autorizzazione per accedere a questa risorsa.\nContatta il tuo amministratore." }, "paymentRequired": { - "title": "402 - Richiesto Aggiornamento del tuo Abbonamento", - "description": "Il tuo abbonamento attuale non supporta l'accesso a questa funzione.

Per favore, aggiorna il tuo abbonamento per continuare." + "title": "Sblocca funzionalità Premium", + "description": "Questa funzionalità è disponibile nei piani avanzati.\nEffettua l'upgrade per espandere le tue capacità." + }, + "error": { + "title": "Qualcosa è andato storto", + "description": "Si è verificato un errore imprevisto. Riprova più tardi." } } diff --git a/packages/common-i18n/src/locales/ja/common.json b/packages/common-i18n/src/locales/ja/common.json index cf475da148..ea7f174c65 100644 --- a/packages/common-i18n/src/locales/ja/common.json +++ b/packages/common-i18n/src/locales/ja/common.json @@ -1,5 +1,7 @@ { "actions": { + "title": "アクション", + "add": "追加", "save": "保存", "doNotSave": "保存しない", "submit": "送信", @@ -14,6 +16,7 @@ "zoomIn": "ズームイン", "zoomOut": "ズームアウト", "back": "戻る", + "remove": "削除", "removeConfig": "設定を削除", "saveSucceed": "保存成功!", "submitSucceed": "送信成功!", @@ -29,12 +32,19 @@ "duplicate": "複製", "change": "変更", "upgrade": "アップグレード", + "upgradeToLevel": "{{level}}にアップグレード", "search": "検索", "loadMore": "されに読み込む", "collapseSidebar": "サイドバーを折りたたむ", "restore": "復元", "permanentDelete": "永久削除", + "globalSearch": "グローバル検索", + "fieldSearch": "フィールドで検索", + "tableIndex": "インデックス", + "showAllRow": "すべての行を表示", + "hideNotMatchRow": "一致しない行を非表示", "more": "もっと", + "expand": "展開", "move": "移動先", "turnOn": "有効にする", "exit": "ログアウト", @@ -43,7 +53,16 @@ "select": "選択", "view": "表示", "preview": "プレビュー", - "viewAndEdit": "表示と編集" + "viewAndEdit": "表示と編集", + "deleteTip": "\"{{name}}\"を削除してもよろしいですか?", + "refresh": "更新", + "login": "ログイン", + "useTemplate": "テンプレートを使用", + "backToSpace": "スペースに戻る", + "switchBase": "ベースを切り替え", + "continue": "続行", + "export": "エクスポート", + "import": "インポート" }, "quickAction": { "title": "クイックアクション...", @@ -52,12 +71,13 @@ "password": { "setInvalid": "パスワードが無効です。少なくとも8文字で、少なくとも1つの文字と1つの数字を含める必要があります。" }, - "non": { - "share": "共有", - "copy": "コピーされました" - }, "template": { + "non": { + "share": "共有", + "copy": "コピーされました" + }, "aiTitle": "一緒に構築しましょう", + "aiGreeting": "{{name}}さん、何かお手伝いできますか?", "aiSubTitle": "チームがデータで協力し、本番アプリを生成する最初のAIプラットフォーム", "guideTitle": "シナリオから始める", "watchVideo": "動画を見る", @@ -68,6 +88,10 @@ "loadMore": "さらに読み込む", "allTemplatesLoaded": "すべてのテンプレートが読み込まれました", "createTemplate": "テンプレートを作成", + "useTemplateDialog": { + "title": "スペースを選択", + "description": "テンプレート用のスペースを選択してください" + }, "promptBox": { "placeholder": "Teableでビジネスアプリを構築", "start": "開始", @@ -83,7 +107,7 @@ } }, "settings": { - "title": "設定", + "title": "インスタンス設定", "personal": { "title": "個人設定" }, @@ -128,6 +152,20 @@ "addPasswordSuccess": { "title": "🎉 パスワードの追加に成功しました。" }, + "deleteAccount": { + "title": "アカウントを削除", + "desc": "このアクションは元に戻せません。アカウントとすべての関連データが完全に削除されます。", + "error": { + "title": "アカウントを削除できません", + "desc": "まず以下の依存関係を処理する必要があります:", + "spacesError": "アカウントを削除する前に、まずスペースを退出(または削除してゴミ箱に移動)する必要があります。" + }, + "confirm": { + "title": "確認するにはDELETEと入力してください", + "placeholder": "DELETE" + }, + "loading": "削除中..." + }, "changeEmail": { "title": "メールアドレスの変更", "desc": "パスワードを認証し、新しいメールアドレスを確認してください", @@ -175,14 +213,40 @@ }, "integration": { "title": "統合", - "description": "は{{count}}のアプリケーションにアカウントへのアクセスを許可しました。", - "lastUsed": "最終使用日:{{date}}", - "revoke": "取り消す", - "owner": "所有者:{{user}}", - "revokeTitle": "本当に承認を取り消しますか?", - "revokeDesc": "{{name}}はTeable APIにアクセスできなくなります。この操作を元に戻すことはできません。", - "scopeTitle": "権限", - "scopeDesc": "このアプリケーションは、次のスコープを取得できます:" + "thirdPartyIntegrations": { + "title": "サードパーティ統合", + "description": "{{count}}個のアプリケーションにアカウントへのアクセスを許可しました。", + "lastUsed": "最終使用日:{{date}}", + "revoke": "取り消す", + "owner": "所有者:{{user}}", + "revokeTitle": "本当に承認を取り消しますか?", + "revokeDesc": "{{name}}はTeable APIにアクセスできなくなります。この操作を元に戻すことはできません。", + "scopeTitle": "権限", + "scopeDesc": "このアプリケーションは、次のスコープを取得できます:" + }, + "userIntegration": { + "title": "接続されたアカウント", + "description": "外部アカウントを接続して、Teableがあなたのリソースにアクセスできるようにします。", + "emptyDescription": "接続されたアカウントがありません", + "actions": { + "reconnect": "再接続" + }, + "slack": { + "user": "Slackユーザー", + "workspace": "Slackワークスペース" + }, + "deleteTitle": "接続されたアカウントを削除", + "deleteDesc": "{{name}}を削除してもよろしいですか?", + "create": "新しいアカウントを接続", + "manage": "接続されたアカウントを管理", + "searchPlaceholder": "接続されたアカウントを検索", + "defaultName": "{{name}}統合", + "callback": { + "error": "認証に失敗しました", + "title": "認証に成功しました", + "desc": "このウィンドウを閉じることができます。" + } + } }, "templateAdmin": { "header": { @@ -234,20 +298,25 @@ "folder": "フォルダ", "newAutomation": "新しいオートメーション", "newApp": "新しいアプリ", - "newFolder": "新しいフォルダ" + "newFolder": "新しいフォルダ", + "template": "テンプレート" }, "level": { "free": "無料", "plus": "プラス", "pro": "プロ", + "business": "ビジネス", "enterprise": "エンタープライズ" }, "noResult": "結果なし。", + "allNodes": "すべてのノード", + "noDescription": "説明なし", "untitled": "無題", "name": "名称", "description": "説明", "required": "必須", "characters": "文字", + "atLeastOne": "少なくとも1つの{{noun}}を保持してください", "guide": { "prev": "前へ", "next": "次へ", @@ -298,10 +367,16 @@ "desc_other": "このベースは{{count}}人の共同作業者と共有されています。", "baseTitle": "ベースの共同作業者", "collaboratorSearchPlaceholder": "名前またはメールアドレスでベースの共同作業者を探す" + }, + "addOrgCollaborator": { + "title": "組織の共同作業者を追加", + "placeholder": "組織のメンバーまたは部門を選択" } }, "help": { "title": "ヘルプ", + "appLink": "https://app.teable.ai", + "mainLink": "https://help.teable.ai", "apiLink": "https://help.teable.ai/en/api-doc/token" }, "pagePermissionChangeTip": "ページの権限が更新されました。最新のコンテンツを表示するにはページを更新してください。", @@ -312,7 +387,11 @@ "userLimitExceededDescription": "現在のインスタンスは、ライセンスで許可されている最大ユーザー数に達しました。一部のユーザーを非アクティブ化するか、ライセンスをアップグレードしてください。", "unavailableInPlanTips": "現在契約中のプランではこの機能はサポートされていません", "unavailableConnectionTips": "データベース接続機能は将来削除され、Enterpriseプランと自己ホストバージョンでのみ利用可能になります。", - "levelTips": "このスペースは現在{{level}}プランにです", + "levelTips": "このスペースは現在{{level}}プランです", + "enterpriseFeature": "エンタープライズ機能", + "automationRequiresUpgrade": "オートメーションを有効にするにはEnterprise Edition(EE)にアップグレードしてください", + "authorityMatrixRequiresUpgrade": "権限マトリックスを有効にするにはEnterprise Edition(EE)にアップグレードしてください", + "viewPricing": "料金を見る", "billable": "課金対象", "billableByAuthorityMatrix": "権限マトリックスによって生成された課金", "licenseExpiredGracePeriod": "セルフホスト版ライセンスの有効期限が切れました。{{expiredTime}}に無料プランへダウングレードされ、プレミアム機能が利用できなくなります。完全な機能を維持するため、速やかにライセンスを更新してください。", @@ -334,6 +413,7 @@ }, "admin": { "setting": { + "instanceTitle": "インスタンス設定", "description": "現在のインスタンスの設定を変更する", "allowSignUp": "新しいアカウントの作成を許可する", "allowSignUpDescription": "このオプションを無効にすると、新規ユーザーの登録が禁止され、ログインページに登録ボタンが表示されなくなります。", @@ -346,10 +426,14 @@ "enableWaitlist": "待機リストを有効にする", "enableWaitlistDescription": "このオプションを有効にすると、ユーザーは招待コードを使用して新規アカウントの作成が可能になります。", "generalSettings": "一般設定", - "aiSettings": "AI設定", + "aiSettings": { + "title": "AI設定", + "description": "このインスタンスのAI設定を構成する" + }, "brandingSettings": { "title": "ブランディング設定", "description": "エンタープライズエディションでのみ利用可能", + "brandName": "ブランド名", "logo": "ロゴ", "logoDescription": "ロゴはTeableでのあなたのブランドアイデンティティです。", "logoUpload": "ロゴをアップロード", @@ -524,6 +608,16 @@ "viewer": "編集もコメントもできません" } }, + "pluginCenter": { + "pluginUrlEmpty": "プラグインにURLがありません", + "install": "インストール", + "publisher": "発行者", + "lastUpdated": "最終更新", + "pluginNotFound": "プラグインが見つかりません", + "pluginEmpty": { + "title": "プラグインがまだありません" + } + }, "trash": { "spaceTrash": "スペースのゴミ箱", "type": "タイプ", diff --git a/packages/common-i18n/src/locales/ja/sdk.json b/packages/common-i18n/src/locales/ja/sdk.json index b09f1edda5..df89618d46 100644 --- a/packages/common-i18n/src/locales/ja/sdk.json +++ b/packages/common-i18n/src/locales/ja/sdk.json @@ -1354,6 +1354,28 @@ "cannotDeployAppBeforeInitialization": "初期化前にアプリをデプロイできません", "noProjectOrVersionFound": "プロジェクトまたはバージョンが見つかりません", "noDeploymentUrlAvailable": "デプロイURLが利用できません" + }, + "reward": { + "notFound": "リワードが見つかりません", + "unsupportedSourceType": "サポートされていないリワードソースタイプです", + "maxClaimsReached": "今週のリワード請求上限(2回)に達しました", + "verificationFailed": "検証に失敗しました: {{errors}}", + "alreadyClaimedThisWeek": "今週すでにこのアカウントのリワードを請求しています", + "invalidPostUrl": "投稿URLの形式が無効です", + "postAlreadyUsed": "この投稿はすでにリワードの請求に使用されています", + "unsupportedPlatformUrl": "サポートされていないソーシャルプラットフォームのURLです", + "unsupportedPlatform": "サポートされていないプラットフォーム: {{platform}}", + "minCharCount": "投稿は少なくとも{{count}}文字必要です", + "minFollowerCount": "アカウントには少なくとも{{count}}人のフォロワーが必要です", + "mustMention": "投稿には{{mention}}へのメンションが必要です", + "fetchTweetFailed": "Xツイートの取得に失敗しました: {{error}}", + "tweetNotFound": "Xツイートが見つかりません: {{postId}}", + "fetchUserFailed": "Xユーザーの取得に失敗しました: {{error}}", + "xUserNotFound": "Xユーザーが見つかりません: {{username}}", + "fetchLinkedInPostFailed": "LinkedIn投稿の取得に失敗しました: {{error}}", + "linkedInPostNotFound": "LinkedIn投稿が見つかりません: {{postId}}", + "linkedInAuthorNotFound": "LinkedIn投稿者が見つかりません: {{postId}}", + "fetchLinkedInUserFailed": "LinkedInユーザーの取得に失敗しました: {{error}}" } } } diff --git a/packages/common-i18n/src/locales/ja/space.json b/packages/common-i18n/src/locales/ja/space.json index df2b22301d..d86820aba0 100644 --- a/packages/common-i18n/src/locales/ja/space.json +++ b/packages/common-i18n/src/locales/ja/space.json @@ -41,6 +41,10 @@ "pin": "ピン", "empty": "ピン留めしたベースとスペースがここに表示されます" }, + "tooltip": { + "noPermissionToCreateBase": "ベースを作成する権限がありません", + "creatingBase": "ベースを作成中..." + }, "tip": { "delete": "<0/> を削除してもよろしいですか?", "title": "ヒント", diff --git a/packages/common-i18n/src/locales/ja/system.json b/packages/common-i18n/src/locales/ja/system.json index 618f0a4747..a179458267 100644 --- a/packages/common-i18n/src/locales/ja/system.json +++ b/packages/common-i18n/src/locales/ja/system.json @@ -1,16 +1,21 @@ { "notFound": { - "title": "404 - ページが見つかりません" + "title": "ページが見つかりません", + "description": "お探しのリンクは無効であるか、ページが移動された可能性があります。" }, "links": { "backToHome": "ホームへ戻る" }, "forbidden": { - "title": "403 - Forbidden", - "description": "このページにアクセスする権限がありません。" + "title": "アクセス制限", + "description": "このリソースにアクセスするには権限が必要です。\n管理者にお問い合わせください。" }, "paymentRequired": { - "title": "402 - 契約プランのアップグレードが必要です", - "description": "現在の契約プランではこの機能にアクセスできません。

続行するには契約プランをアップグレードしてください。" + "title": "プレミアム機能のロック解除", + "description": "この機能は上位プランでご利用いただけます。\nアップグレードして機能を拡張しましょう。" + }, + "error": { + "title": "エラーが発生しました", + "description": "予期しないエラーが発生しました。後でもう一度お試しください。" } } diff --git a/packages/common-i18n/src/locales/ru/common.json b/packages/common-i18n/src/locales/ru/common.json index 83ce77e253..7c7dd04b52 100644 --- a/packages/common-i18n/src/locales/ru/common.json +++ b/packages/common-i18n/src/locales/ru/common.json @@ -1,6 +1,7 @@ { "actions": { - "title": "действия", + "title": "Действия", + "add": "Добавить", "save": "Сохранить", "doNotSave": "Не сохранять", "submit": "Отправить", @@ -15,6 +16,7 @@ "zoomIn": "Увеличить", "zoomOut": "Уменьшить", "back": "Назад", + "remove": "Удалить", "removeConfig": "Удалить конфигурацию", "saveSucceed": "Сохранение успешно!", "submitSucceed": "Отправка успешно!", @@ -30,12 +32,19 @@ "duplicate": "Дублировать", "change": "Изменить", "upgrade": "Обновить", + "upgradeToLevel": "Обновить до {{level}}", "search": "Поиск", "loadMore": "Загрузить больше", "collapseSidebar": "Свернуть боковую панель", "restore": "Восстановить", "permanentDelete": "Удалить навсегда", + "globalSearch": "Глобальный поиск", + "fieldSearch": "Поиск по полю", + "tableIndex": "Индекс", + "showAllRow": "Показать все строки", + "hideNotMatchRow": "Скрыть несоответствующие строки", "more": "Больше", + "expand": "Развернуть", "move": "Переместить в", "turnOn": "Включить", "exit": "Выйти", @@ -44,7 +53,16 @@ "select": "Выбрать", "view": "Просмотр", "preview": "Предпросмотр", - "viewAndEdit": "Просмотр и редактирование" + "viewAndEdit": "Просмотр и редактирование", + "deleteTip": "Вы уверены, что хотите удалить \"{{name}}\"?", + "refresh": "Обновить", + "login": "Войти", + "useTemplate": "Использовать шаблон", + "backToSpace": "Вернуться в пространство", + "switchBase": "Переключить базу", + "continue": "Продолжить", + "export": "Экспорт", + "import": "Импорт" }, "quickAction": { "title": "Быстрые действия...", @@ -53,12 +71,13 @@ "password": { "setInvalid": "Пароль недействителен, должен содержать не менее 8 символов и хотя бы одну букву и одну цифру." }, - "non": { - "share": "Поделиться", - "copy": "Скопировано" - }, "template": { + "non": { + "share": "Поделиться", + "copy": "Скопировано" + }, "aiTitle": "Давайте создадим вместе", + "aiGreeting": "Чем я могу помочь, {{name}}?", "aiSubTitle": "Первая AI-платформа, где команды сотрудничают над данными и создают производственные приложения", "guideTitle": "Начните со своего сценария", "watchVideo": "Смотреть видео", @@ -69,6 +88,10 @@ "loadMore": "Загрузить Больше", "allTemplatesLoaded": "Все шаблоны загружены", "createTemplate": "Создать Шаблон", + "useTemplateDialog": { + "title": "Выберите пространство", + "description": "Выберите пространство для шаблона" + }, "promptBox": { "placeholder": "Создайте бизнес-приложение с Teable", "start": "Начать", @@ -84,7 +107,7 @@ } }, "settings": { - "title": "Настройки", + "title": "Настройки экземпляра", "personal": { "title": "Личные настройки" }, @@ -129,6 +152,20 @@ "addPasswordSuccess": { "title": "🎉 Пароль успешно добавлен." }, + "deleteAccount": { + "title": "Удалить учетную запись", + "desc": "Это действие необратимо. Ваша учетная запись и все связанные данные будут удалены навсегда.", + "error": { + "title": "Невозможно удалить учетную запись", + "desc": "Сначала необходимо обработать следующие зависимости:", + "spacesError": "Перед удалением учетной записи необходимо сначала выйти (или удалить, а затем переместить в корзину) из ваших пространств." + }, + "confirm": { + "title": "Введите DELETE для подтверждения", + "placeholder": "DELETE" + }, + "loading": "Удаление..." + }, "changeEmail": { "title": "Сменить адрес электронной почты", "desc": "Пожалуйста, подтвердите пароль и новый адрес электронной почты", @@ -235,20 +272,25 @@ "folder": "Папка", "newAutomation": "Новая автоматизация", "newApp": "Новое приложение", - "newFolder": "Новая папка" + "newFolder": "Новая папка", + "template": "Шаблон" }, "level": { "free": "Бесплатно", "plus": "Плюс", "pro": "Профи", + "business": "Бизнес", "enterprise": "Предприятие" }, "noResult": "Нет результата.", + "allNodes": "Все узлы", + "noDescription": "Нет описания", "untitled": "Без названия", "name": "Имя", "description": "Описание", "required": "Обязательно", "characters": "символов", + "atLeastOne": "Сохраните хотя бы один {{noun}}", "guide": { "prev": "Назад", "next": "Далее", @@ -303,6 +345,8 @@ }, "help": { "title": "Помощь", + "appLink": "https://app.teable.ai", + "mainLink": "https://help.teable.ai", "apiLink": "https://help.teable.ai/en/api-doc/token" }, "pagePermissionChangeTip": "Разрешения страницы были обновлены. Пожалуйста, обновите страницу, чтобы увидеть последние изменения.", @@ -314,6 +358,10 @@ "unavailableInPlanTips": "Текущий тарифный план не поддерживает эту функцию", "unavailableConnectionTips": "Функция подключения к базе данных будет удалена в будущем и будет доступна только в тарифном плане Enterprise и в самохостинг версии.", "levelTips": "Это пространство сейчас на тарифе {{level}}", + "enterpriseFeature": "Функция Enterprise", + "automationRequiresUpgrade": "Обновитесь до Enterprise Edition (EE) чтобы включить автоматизацию", + "authorityMatrixRequiresUpgrade": "Обновитесь до Enterprise Edition (EE) чтобы включить матрицу полномочий", + "viewPricing": "Посмотреть цены", "billable": "Платный", "billableByAuthorityMatrix": "Биллинг, созданный матрицей полномочий", "licenseExpiredGracePeriod": "Срок действия вашей лицензии для самостоятельного размещения истек и будет понижена до бесплатного плана {{expiredTime}}. Пожалуйста, обновите лицензию, чтобы сохранить доступ к премиум-функциям.", @@ -335,6 +383,7 @@ }, "admin": { "setting": { + "instanceTitle": "Настройки экземпляра", "description": "Измените настройки для текущего экземпляра", "allowSignUp": "Разрешить создание новых аккаунтов", "allowSignUpDescription": "Отключение этой опции запретит регистрацию новых пользователей, и кнопка регистрации больше не будет отображаться на странице входа.", @@ -347,10 +396,14 @@ "enableWaitlist": "Включить список ожидания", "enableWaitlistDescription": "Включение этой опции позволит пользователям регистрироваться только с помощью приглашения.", "generalSettings": "Общие настройки", - "aiSettings": "AI настройки", + "aiSettings": { + "title": "AI настройки", + "description": "Настроить AI параметры для этого экземпляра" + }, "brandingSettings": { "title": "Настройки брендинга", "description": "Доступно только в корпоративной версии", + "brandName": "Название бренда", "logo": "Логотип", "logoDescription": "Логотип - это ваша фирменная идентичность в Teable.", "logoUpload": "Загрузить логотип", diff --git a/packages/common-i18n/src/locales/ru/sdk.json b/packages/common-i18n/src/locales/ru/sdk.json index 76fddcaa5b..efee59791f 100644 --- a/packages/common-i18n/src/locales/ru/sdk.json +++ b/packages/common-i18n/src/locales/ru/sdk.json @@ -1354,6 +1354,28 @@ "cannotDeployAppBeforeInitialization": "Невозможно развернуть приложение до инициализации", "noProjectOrVersionFound": "Проект или версия не найдены", "noDeploymentUrlAvailable": "URL развертывания недоступен" + }, + "reward": { + "notFound": "Награда не найдена", + "unsupportedSourceType": "Неподдерживаемый тип источника награды", + "maxClaimsReached": "Вы достигли максимального количества запросов наград (2) на этой неделе", + "verificationFailed": "Ошибка проверки: {{errors}}", + "alreadyClaimedThisWeek": "Вы уже запросили награду для этого аккаунта на этой неделе", + "invalidPostUrl": "Неверный формат URL публикации", + "postAlreadyUsed": "Эта публикация уже использовалась для получения награды", + "unsupportedPlatformUrl": "Неподдерживаемый URL социальной платформы", + "unsupportedPlatform": "Неподдерживаемая платформа: {{platform}}", + "minCharCount": "Публикация должна содержать не менее {{count}} символов", + "minFollowerCount": "Аккаунт должен иметь не менее {{count}} подписчиков", + "mustMention": "Публикация должна упоминать {{mention}}", + "fetchTweetFailed": "Не удалось получить твит X: {{error}}", + "tweetNotFound": "Твит X не найден: {{postId}}", + "fetchUserFailed": "Не удалось получить пользователя X: {{error}}", + "xUserNotFound": "Пользователь X не найден: {{username}}", + "fetchLinkedInPostFailed": "Не удалось получить публикацию LinkedIn: {{error}}", + "linkedInPostNotFound": "Публикация LinkedIn не найдена: {{postId}}", + "linkedInAuthorNotFound": "Автор LinkedIn не найден: {{postId}}", + "fetchLinkedInUserFailed": "Не удалось получить пользователя LinkedIn: {{error}}" } } } diff --git a/packages/common-i18n/src/locales/ru/space.json b/packages/common-i18n/src/locales/ru/space.json index 3ed4ff4bbe..048dd2b6e8 100644 --- a/packages/common-i18n/src/locales/ru/space.json +++ b/packages/common-i18n/src/locales/ru/space.json @@ -41,6 +41,10 @@ "pin": "Закрепить", "empty": "Ваши закрепленные базы и пространства появятся здесь" }, + "tooltip": { + "noPermissionToCreateBase": "У вас нет разрешения на создание базы", + "creatingBase": "Создание базы..." + }, "tip": { "delete": "Вы уверены, что хотите удалить <0/>?", "title": "Советы", diff --git a/packages/common-i18n/src/locales/ru/system.json b/packages/common-i18n/src/locales/ru/system.json index 87ce370538..a6dcc60e1f 100644 --- a/packages/common-i18n/src/locales/ru/system.json +++ b/packages/common-i18n/src/locales/ru/system.json @@ -1,16 +1,21 @@ { "notFound": { - "title": "404 - Страница не найдена" + "title": "Страница не найдена", + "description": "Ссылка, по которой вы перешли, может быть недействительной, или страница была перемещена." }, "links": { "backToHome": "Вернуться на главную" }, "forbidden": { - "title": "403 - Доступ запрещен", - "description": "У вас нет разрешения для доступа к этой странице." + "title": "Доступ ограничен", + "description": "Для доступа к этому ресурсу требуется разрешение.\nПожалуйста, обратитесь к администратору." }, "paymentRequired": { - "title": "402 - Требуется обновление подписки", - "description": "Ваша текущая подписка не поддерживает доступ к этой функции.

Пожалуйста, обновите подписку, чтобы продолжить." + "title": "Разблокировать премиум-функцию", + "description": "Эта функция доступна в расширенных тарифах.\nОбновите план, чтобы расширить возможности." + }, + "error": { + "title": "Что-то пошло не так", + "description": "Произошла непредвиденная ошибка. Пожалуйста, попробуйте позже." } } diff --git a/packages/common-i18n/src/locales/tr/common.json b/packages/common-i18n/src/locales/tr/common.json index dfeeeab730..251d6dd06c 100644 --- a/packages/common-i18n/src/locales/tr/common.json +++ b/packages/common-i18n/src/locales/tr/common.json @@ -1,6 +1,7 @@ { "actions": { - "title": "işlemler", + "title": "İşlemler", + "add": "Ekle", "save": "Kaydet", "doNotSave": "Kaydetme", "submit": "Gönder", @@ -15,6 +16,7 @@ "zoomIn": "Yakınlaştır", "zoomOut": "Uzaklaştır", "back": "Geri", + "remove": "Kaldır", "removeConfig": "Yapılandırmayı sil", "saveSucceed": "Başarıyla Kaydedildi!", "submitSucceed": "Başarıyla Gönderildi!", @@ -30,6 +32,7 @@ "duplicate": "Çoğalt", "change": "Değiştir", "upgrade": "Yükselt", + "upgradeToLevel": "{{level}}'e yükselt", "search": "Ara", "loadMore": "Daha Fazla Yükle", "collapseSidebar": "Kenar Çubuğunu Daralt", @@ -39,6 +42,9 @@ "fieldSearch": "Alan Ara", "showAllRow": "Tüm satırları göster", "hideNotMatchRow": "Eşleşmeyen satırları gizle", + "more": "Daha Fazla", + "expand": "Genişlet", + "tableIndex": "Dizin", "move": "Taşı", "turnOn": "Aç", "exit": "Çıkış", @@ -47,7 +53,16 @@ "select": "Seç", "view": "Görüntüle", "preview": "Önizleme", - "viewAndEdit": "Görüntüle ve düzenle" + "viewAndEdit": "Görüntüle ve düzenle", + "deleteTip": "\"{{name}}\" öğesini silmek istediğinizden emin misiniz?", + "refresh": "Yenile", + "login": "Giriş Yap", + "useTemplate": "Şablon Kullan", + "backToSpace": "Alana Dön", + "switchBase": "Veritabanı Değiştir", + "continue": "Devam Et", + "export": "Dışa Aktar", + "import": "İçe Aktar" }, "quickAction": { "title": "Hızlı İşlemler...", @@ -56,12 +71,13 @@ "password": { "setInvalid": "Şifre geçersiz, en az 8 karakter olmalı ve en az bir harf ve bir rakam içermelidir." }, - "non": { - "share": "Paylaş", - "copy": "Kopyalandı" - }, "template": { + "non": { + "share": "Paylaş", + "copy": "Kopyalandı" + }, "aiTitle": "Birlikte inşa edelim", + "aiGreeting": "Size nasıl yardımcı olabilirim, {{name}}?", "aiSubTitle": "Ekiplerin veriler üzerinde işbirliği yaptığı ve üretim uygulamaları oluşturduğu ilk AI platformu", "guideTitle": "Senaryonuzdan başlayın", "watchVideo": "Video izle", @@ -72,6 +88,10 @@ "loadMore": "Daha Fazla Yükle", "allTemplatesLoaded": "Tüm şablonlar yüklendi", "createTemplate": "Şablon Oluştur", + "useTemplateDialog": { + "title": "Alan Seçin", + "description": "Şablon için bir alan seçin" + }, "promptBox": { "placeholder": "Teable ile iş uygulamanızı oluşturun", "start": "Başlat", @@ -87,7 +107,7 @@ } }, "settings": { - "title": "Ayarlar", + "title": "Örnek Ayarları", "personal": { "title": "Kişisel ayarlar" }, @@ -132,6 +152,20 @@ "addPasswordSuccess": { "title": "🎉 Şifre başarıyla eklendi." }, + "deleteAccount": { + "title": "Hesabı Sil", + "desc": "Bu işlem geri alınamaz. Hesabınız ve tüm ilişkili veriler kalıcı olarak silinecektir.", + "error": { + "title": "Hesap silinemedi", + "desc": "Önce aşağıdaki bağımlılıkları çözmeniz gerekiyor:", + "spacesError": "Hesabınızı silmeden önce, Alanlarınızdan çıkmanız (veya silip çöp kutusuna taşımanız) gerekiyor." + }, + "confirm": { + "title": "Onaylamak için DELETE yazın", + "placeholder": "DELETE" + }, + "loading": "Siliniyor..." + }, "changeEmail": { "title": "E-posta adresini değiştir", "desc": "Lütfen şifrenizi doğrulayın ve yeni e-posta adresinizi onaylayın", @@ -238,15 +272,19 @@ "folder": "Klasör", "newAutomation": "Yeni otomasyon", "newApp": "Yeni uygulama", - "newFolder": "Yeni klasör" + "newFolder": "Yeni klasör", + "template": "Şablon" }, "level": { "free": "Ücretsiz", "plus": "Plus", "pro": "Pro", + "business": "İş", "enterprise": "Kurumsal" }, "noResult": "Sonuç Bulunamadı.", + "allNodes": "Tüm Düğümler", + "noDescription": "Açıklama yok", "untitled": "Başlıksız", "name": "İsim", "description": "Açıklama", @@ -307,6 +345,8 @@ }, "help": { "title": "Yardım", + "appLink": "https://app.teable.ai", + "mainLink": "https://help.teable.ai", "apiLink": "https://help.teable.ai/en/api-doc/token" }, "pagePermissionChangeTip": "Sayfa izinleri güncellendi. En son içeriği görmek için lütfen sayfayı yenileyin.", @@ -724,5 +764,36 @@ }, "base": { "deleteTip": "\"{{name}}\" veritabanını silmek istediğinizden emin misiniz?" - } + }, + "clickToCopyTooltip": "Panoya kopyalamak için tıklayın", + "copiedTooltip": "Kopyalandı!", + "hiddenFieldCount_one": "{{count}} gizli alan", + "hiddenFieldCount_other": "{{count}} gizli alan", + "invalidFieldMapping": "Geçersiz alan eşlemesi", + "sourceFieldNotFoundMapping": "Kaynak alan bulunamadı", + "targetFieldNotFoundMapping": "Hedef alan bulunamadı", + "fieldTypeNotSupportedMapping": "Alan türü desteklenmiyor", + "fieldSettingsNotMatchMapping": "Alan ayarları eşleşmiyor", + "fieldSettingsLookupNotMatch": "Arama alan ayarları eşleşmiyor", + "fieldSettingsLinkTableNotMatch": "Bağlantı tablosu eşleşmiyor", + "fieldSettingsLinkViewNotMatch": "Bağlantı görünümü eşleşmiyor", + "fieldTypeDifferentMapping": "Alan türleri farklı", + "fieldMappingSourceTip": "Kaynak alanları hedef alanlarla eşleyin", + "fieldMappingTargetTip": "Alanları hedefle eşleştirme", + "reset": "Sıfırla", + "checkAll": "Tümünü seç", + "uncheckAll": "Seçimi kaldır", + "duplicateOptionsMapping": "Yinelenen seçenekler", + "lookupFieldInvalidMapping": "Arama alanı geçersiz", + "noMatchedOptions": "Eşleşen seçenek yok", + "needManualSelectionMapping": "Manuel seçim gerekli", + "targetFieldIsComputed": "Hedef alan hesaplanmış", + "targetFieldIsComputedTips": "Hedef alan hesaplanmış ve değiştirilemez", + "emptyOption": "(boş)", + "showEmptyTip": "Boş seçenekleri göster", + "hideEmptyTip": "Boş seçenekleri gizle", + "hideText": "Metni Gizle", + "showText": "Metni Göster", + "sourceTable": "Kaynak Tablo", + "sourceView": "Kaynak Görünüm" } diff --git a/packages/common-i18n/src/locales/tr/sdk.json b/packages/common-i18n/src/locales/tr/sdk.json index 591ee74090..748856d956 100644 --- a/packages/common-i18n/src/locales/tr/sdk.json +++ b/packages/common-i18n/src/locales/tr/sdk.json @@ -1354,6 +1354,28 @@ "cannotDeployAppBeforeInitialization": "Başlatmadan önce uygulama dağıtılamaz", "noProjectOrVersionFound": "Proje veya sürüm bulunamadı", "noDeploymentUrlAvailable": "Dağıtım URL'si mevcut değil" + }, + "reward": { + "notFound": "Ödül bulunamadı", + "unsupportedSourceType": "Desteklenmeyen ödül kaynak türü", + "maxClaimsReached": "Bu hafta için maksimum ödül talebine (2) ulaştınız", + "verificationFailed": "Doğrulama başarısız: {{errors}}", + "alreadyClaimedThisWeek": "Bu hesap için bu hafta zaten bir ödül talep ettiniz", + "invalidPostUrl": "Geçersiz gönderi URL formatı", + "postAlreadyUsed": "Bu gönderi zaten bir ödül talep etmek için kullanıldı", + "unsupportedPlatformUrl": "Desteklenmeyen sosyal platform URL'si", + "unsupportedPlatform": "Desteklenmeyen platform: {{platform}}", + "minCharCount": "Gönderi en az {{count}} karakter içermelidir", + "minFollowerCount": "Hesapta en az {{count}} takipçi olmalıdır", + "mustMention": "Gönderi {{mention}} kişisinden bahsetmelidir", + "fetchTweetFailed": "X tweeti alınamadı: {{error}}", + "tweetNotFound": "X tweeti bulunamadı: {{postId}}", + "fetchUserFailed": "X kullanıcısı alınamadı: {{error}}", + "xUserNotFound": "X kullanıcısı bulunamadı: {{username}}", + "fetchLinkedInPostFailed": "LinkedIn gönderisi alınamadı: {{error}}", + "linkedInPostNotFound": "LinkedIn gönderisi bulunamadı: {{postId}}", + "linkedInAuthorNotFound": "LinkedIn yazarı bulunamadı: {{postId}}", + "fetchLinkedInUserFailed": "LinkedIn kullanıcısı alınamadı: {{error}}" } } } diff --git a/packages/common-i18n/src/locales/tr/space.json b/packages/common-i18n/src/locales/tr/space.json index aeacfb3d85..482d2132f6 100644 --- a/packages/common-i18n/src/locales/tr/space.json +++ b/packages/common-i18n/src/locales/tr/space.json @@ -41,6 +41,10 @@ "pin": "Sabitle", "empty": "Sabitlediğiniz veritabanları ve alanlar burada görünecek" }, + "tooltip": { + "noPermissionToCreateBase": "Veritabanı oluşturma izniniz yok", + "creatingBase": "Veritabanı oluşturuluyor..." + }, "tip": { "delete": "<0/> silmek istediğinizden emin misiniz?", "title": "İpuçları", diff --git a/packages/common-i18n/src/locales/tr/system.json b/packages/common-i18n/src/locales/tr/system.json index d39ba695b3..c569f42d09 100644 --- a/packages/common-i18n/src/locales/tr/system.json +++ b/packages/common-i18n/src/locales/tr/system.json @@ -1,16 +1,21 @@ { "notFound": { - "title": "404 - Sayfa Bulunamadı" + "title": "Sayfa bulunamadı", + "description": "Takip ettiğiniz bağlantı bozuk olabilir veya sayfa taşınmış olabilir." }, "links": { "backToHome": "Ana sayfaya dön" }, "forbidden": { - "title": "403 - Erişim Yasak", - "description": "Bu sayfaya erişim izniniz yok." + "title": "Erişim kısıtlı", + "description": "Bu kaynağa erişmek için izne ihtiyacınız var.\nLütfen yöneticinizle iletişime geçin." }, "paymentRequired": { - "title": "402 - Abonelik Yükseltmesi Gerekli", - "description": "Mevcut aboneliğiniz bu özelliğe erişimi desteklemiyor.

Devam etmek için lütfen aboneliğinizi yükseltin." + "title": "Premium özelliğin kilidini aç", + "description": "Bu özellik gelişmiş planlarda kullanılabilir.\nYeteneklerinizi genişletmek için yükseltin." + }, + "error": { + "title": "Bir şeyler yanlış gitti", + "description": "Beklenmeyen bir hata oluştu. Lütfen daha sonra tekrar deneyin." } } diff --git a/packages/common-i18n/src/locales/uk/common.json b/packages/common-i18n/src/locales/uk/common.json index 2dd396b6f9..28ae8f70df 100644 --- a/packages/common-i18n/src/locales/uk/common.json +++ b/packages/common-i18n/src/locales/uk/common.json @@ -30,6 +30,7 @@ "duplicate": "Дублювати", "change": "Змінити", "upgrade": "Оновити", + "upgradeToLevel": "Оновити до {{level}}", "search": "Пошук", "loadMore": "Завантажити більше", "collapseSidebar": "Згорнути бічну панель", @@ -84,7 +85,7 @@ } }, "settings": { - "title": "Налаштування", + "title": "Приклад налаштувань", "personal": { "title": "Особисті налаштування" }, @@ -127,6 +128,20 @@ "addPasswordSuccess": { "title": "🎉 Пароль додано успішно." }, + "deleteAccount": { + "title": "Видалити обліковий запис", + "desc": "Цю дію неможливо скасувати. Ваш обліковий запис і всі пов'язані дані будуть назавжди видалені.", + "error": { + "title": "Не вдалося видалити обліковий запис", + "desc": "Вам потрібно спочатку вирішити наступні залежності:", + "spacesError": "Перш ніж видалити обліковий запис, вам потрібно вийти з просторів (або видалити та перемістити в кошик)." + }, + "confirm": { + "title": "Введіть DELETE для підтвердження", + "placeholder": "DELETE" + }, + "loading": "Видалення..." + }, "changeEmail": { "title": "Змінити адресу електронної пошти", "desc": "Будь ласка, перевірте пароль і підтвердьте нову адресу електронної пошти", @@ -233,15 +248,19 @@ "folder": "Папка", "newAutomation": "Нова автоматизація", "newApp": "Новий додаток", - "newFolder": "Нова папка" + "newFolder": "Нова папка", + "template": "Шаблон" }, "level": { "free": "Безкоштовно", "plus": "Плюс", "pro": "Профі", + "business": "Бізнес", "enterprise": "Підприємство" }, "noResult": "Немає результату.", + "allNodes": "Усі вузли", + "noDescription": "Без опису", "untitled": "Без назви", "name": "Назва", "description": "Опис", @@ -306,6 +325,8 @@ }, "help": { "title": "Допомога", + "appLink": "https://app.teable.ai", + "mainLink": "https://help.teable.ai", "apiLink": "https://help.teable.ai/en/api-doc/token" }, "pagePermissionChangeTip": "Дозволи сторінки оновлено. Будь ласка, оновіть сторінку, щоб побачити найновіший вміст.", @@ -723,5 +744,36 @@ }, "base": { "deleteTip": "Ви впевнені, що хочете видалити \"{{name}}\" базу?" - } + }, + "clickToCopyTooltip": "Клацніть, щоб скопіювати в буфер обміну", + "copiedTooltip": "Скопійовано!", + "hiddenFieldCount_one": "{{count}} приховане поле", + "hiddenFieldCount_other": "{{count}} прихованих полів", + "invalidFieldMapping": "Недійсне зіставлення поля", + "sourceFieldNotFoundMapping": "Поле джерела не знайдено", + "targetFieldNotFoundMapping": "Цільове поле не знайдено", + "fieldTypeNotSupportedMapping": "Тип поля не підтримується", + "fieldSettingsNotMatchMapping": "Налаштування поля не збігаються", + "fieldSettingsLookupNotMatch": "Налаштування поля пошуку не збігаються", + "fieldSettingsLinkTableNotMatch": "Зв'язана таблиця не збігається", + "fieldSettingsLinkViewNotMatch": "Зв'язаний вигляд не збігається", + "fieldTypeDifferentMapping": "Типи полів різні", + "fieldMappingSourceTip": "Зіставте поля джерела з цільовими полями", + "fieldMappingTargetTip": "Зіставте поля з ціллю", + "reset": "Скинути", + "checkAll": "Вибрати все", + "uncheckAll": "Скасувати вибір", + "duplicateOptionsMapping": "Повторювані варіанти", + "lookupFieldInvalidMapping": "Поле пошуку недійсне", + "noMatchedOptions": "Немає відповідних варіантів", + "needManualSelectionMapping": "Потрібен ручний вибір", + "targetFieldIsComputed": "Цільове поле обчислюване", + "targetFieldIsComputedTips": "Цільове поле обчислюване і не може бути змінене", + "emptyOption": "(порожньо)", + "showEmptyTip": "Показати порожні варіанти", + "hideEmptyTip": "Приховати порожні варіанти", + "hideText": "Приховати текст", + "showText": "Показати текст", + "sourceTable": "Таблиця джерела", + "sourceView": "Вигляд джерела" } diff --git a/packages/common-i18n/src/locales/uk/sdk.json b/packages/common-i18n/src/locales/uk/sdk.json index 9ad030533c..7f1006639b 100644 --- a/packages/common-i18n/src/locales/uk/sdk.json +++ b/packages/common-i18n/src/locales/uk/sdk.json @@ -1354,6 +1354,28 @@ "cannotDeployAppBeforeInitialization": "Неможливо розгорнути додаток до ініціалізації", "noProjectOrVersionFound": "Проект або версію не знайдено", "noDeploymentUrlAvailable": "URL розгортання недоступний" + }, + "reward": { + "notFound": "Нагороду не знайдено", + "unsupportedSourceType": "Непідтримуваний тип джерела нагороди", + "maxClaimsReached": "Ви досягли максимальної кількості запитів нагород (2) на цьому тижні", + "verificationFailed": "Помилка перевірки: {{errors}}", + "alreadyClaimedThisWeek": "Ви вже запитали нагороду для цього акаунту на цьому тижні", + "invalidPostUrl": "Недійсний формат URL публікації", + "postAlreadyUsed": "Ця публікація вже використовувалась для отримання нагороди", + "unsupportedPlatformUrl": "Непідтримуваний URL соціальної платформи", + "unsupportedPlatform": "Непідтримувана платформа: {{platform}}", + "minCharCount": "Публікація повинна містити щонайменше {{count}} символів", + "minFollowerCount": "Акаунт повинен мати щонайменше {{count}} підписників", + "mustMention": "Публікація повинна згадувати {{mention}}", + "fetchTweetFailed": "Не вдалося отримати твіт X: {{error}}", + "tweetNotFound": "Твіт X не знайдено: {{postId}}", + "fetchUserFailed": "Не вдалося отримати користувача X: {{error}}", + "xUserNotFound": "Користувача X не знайдено: {{username}}", + "fetchLinkedInPostFailed": "Не вдалося отримати публікацію LinkedIn: {{error}}", + "linkedInPostNotFound": "Публікацію LinkedIn не знайдено: {{postId}}", + "linkedInAuthorNotFound": "Автора LinkedIn не знайдено: {{postId}}", + "fetchLinkedInUserFailed": "Не вдалося отримати користувача LinkedIn: {{error}}" } } } diff --git a/packages/common-i18n/src/locales/uk/space.json b/packages/common-i18n/src/locales/uk/space.json index b7db947f7f..ee3a3d3e85 100644 --- a/packages/common-i18n/src/locales/uk/space.json +++ b/packages/common-i18n/src/locales/uk/space.json @@ -41,6 +41,10 @@ "pin": "Закріпити", "empty": "Тут з'являться ваші закріплені бази та простори" }, + "tooltip": { + "noPermissionToCreateBase": "У вас немає дозволу створювати базу", + "creatingBase": "Створення бази..." + }, "tip": { "delete": "Ви впевнені, що хочете видалити <0/>?", "title": "Поради", diff --git a/packages/common-i18n/src/locales/uk/system.json b/packages/common-i18n/src/locales/uk/system.json index b0bd8d195d..18c74b688a 100644 --- a/packages/common-i18n/src/locales/uk/system.json +++ b/packages/common-i18n/src/locales/uk/system.json @@ -1,16 +1,21 @@ { "notFound": { - "title": "404 - Сторінку не знайдено" + "title": "Сторінку не знайдено", + "description": "Посилання, за яким ви перейшли, може бути недійсним, або сторінку переміщено." }, "links": { - "backToHome": "Додому" + "backToHome": "Повернутися на головну" }, "forbidden": { - "title": "403 - Заборонено.", - "description": "Ви не маєте дозволу на доступ до цієї сторінки." + "title": "Доступ обмежено", + "description": "Вам потрібен дозвіл для доступу до цього ресурсу.\nБудь ласка, зверніться до адміністратора." }, "paymentRequired": { - "title": "402 - Необхідно оновити підписку", - "description": "Ваша поточна підписка не підтримує доступ до цієї функції.

Оновіть свою підписку, щоб продовжити." + "title": "Розблокувати преміум-функцію", + "description": "Ця функція доступна в розширених тарифах.\nОновіть план, щоб розширити можливості." + }, + "error": { + "title": "Щось пішло не так", + "description": "Сталася неочікувана помилка. Будь ласка, спробуйте пізніше." } } diff --git a/packages/common-i18n/src/locales/zh/common.json b/packages/common-i18n/src/locales/zh/common.json index d403673a72..84e39e60d8 100644 --- a/packages/common-i18n/src/locales/zh/common.json +++ b/packages/common-i18n/src/locales/zh/common.json @@ -35,6 +35,7 @@ "import": "导入", "change": "变更", "upgrade": "升级", + "upgradeToLevel": "升级到{{level}}", "search": "搜索", "loadMore": "加载更多", "collapseSidebar": "折叠侧边栏", @@ -59,7 +60,9 @@ "select": "选择", "refresh": "刷新", "login": "登录", - "useTemplate": "使用模版" + "useTemplate": "使用模版", + "backToSpace": "返回空间", + "switchBase": "切换数据库" }, "quickAction": { "title": "快捷搜索...", @@ -948,6 +951,16 @@ "unknown": "表 \"{{tableName}}\" 的 AI 任务已取消。错误:{{errorMessage}}" } } + }, + "rewardRejected": { + "title": "奖励领取被拒绝", + "message": "您的 {{spaceName}} 奖励申请因以下原因被拒绝:{{errorMessages}},请检查后重新提交。", + "buttonText": "查看空间" + }, + "rewardApproved": { + "title": "奖励领取成功", + "message": "恭喜!您的 {{spaceName}} 奖励申请已通过。{{amount}} 算力已添加,有效期 {{expiredDays}} 天。", + "buttonText": "查看空间" } } } @@ -985,5 +998,103 @@ "description": "配置 v0 API 密钥以启用应用构建器功能,访问 v0 设置 获取 API 密钥", "previewAppError": "应用运行报错", "sendErrorToAI": "将错误发送给 AI" + }, + "credit": { + "title": "算力", + "leftAmount": "剩余算力", + "winFreeCredits": "获取免费算力", + "getCredits": "获取 1000 算力", + "winCredit": { + "title": "分享正面评价即可获得", + "freeCredits": "1000 免费算力", + "guidelinesTitle": "分享要求", + "tagTeableio": "标记 @teableio", + "minCharacters": "撰写 100+ 字符的评价", + "minFollowers": "拥有 10+ 粉丝数", + "limitPerWeek": "每个帖子 500 算力,每周最多 2 次(X + LinkedIn)", + "postOnX": "发布到 X", + "postOnLinkedIn": "发布到 LinkedIn", + "preFilledDraft": "🌟 已为您准备好预填草稿!", + "claimTitle": "粘贴帖子链接领取算力", + "userEmail": "用户邮箱", + "postUrlLabel": "帖子链接 (X 或 LinkedIn)", + "postUrlPlaceholder": "在此粘贴您的帖子链接", + "invalidUrl": "请输入有效的 X 或 LinkedIn 帖子链接", + "claiming": "领取中...", + "claimCredits": "领取算力", + "congratulations": "恭喜!", + "claimSuccess": "您已成功领取 500 算力!", + "verifying": "正在验证您的帖子...", + "verifyingDescription": "我们正在检查您的帖子内容,通常需要几秒钟", + "verifyFailed": "验证失败", + "tryAgain": "重试" + }, + "error": { + "verificationFailed": "验证失败" + } + }, + "reward": { + "title": "奖励", + "rewardCredits": "算力奖励", + "minCharCount": "帖子内容至少需要 {{count}} 个字符", + "minFollowerCount": "账户至少需要 {{count}} 位粉丝", + "mustMention": "帖子必须提及 {{mention}}", + "fetchSnapshotFailed": "获取帖子快照失败", + "alreadyClaimedThisWeek": "您本周已为此账户领取过奖励", + "manage": { + "title": "奖励管理", + "description": "查看和管理所有空间的奖励记录", + "overview": "概览", + "records": "记录", + "searchSpace": "搜索空间...", + "searchRecords": "搜索帖子链接/用户ID...", + "dateRange": "选择日期范围", + "from": "从", + "to": "至", + "totalSpaces": "共 {{count}} 个空间", + "totalRecords": "共 {{count}} 条记录", + "space": "空间", + "allSpaces": "全部空间", + "user": "用户", + "creator": "创建者", + "platform": "平台", + "allStatuses": "全部状态", + "allPlatforms": "全部平台", + "pendingCount": "待处理个数", + "approvedCount": "通过个数", + "rejectedCount": "拒绝个数", + "approvedAmount": "通过数量", + "consumedAmount": "已消耗数量", + "availableAmount": "可用数量", + "expiringSoonAmount": "即将过期数量 (7天)", + "amount": "数量", + "remainingAmount": "剩余数量", + "createdTime": "创建时间", + "rewardTime": "奖励时间", + "expiredTime": "过期时间", + "lastModified": "最后修改", + "viewDetails": "查看详情", + "details": "奖励详情", + "basicInfo": "基本信息", + "amountInfo": "金额信息", + "timeInfo": "时间信息", + "socialInfo": "社交信息", + "verifyResult": "验证结果", + "uniqueKey": "唯一标识", + "verify": "验证", + "valid": "有效", + "invalid": "无效", + "errors": "错误", + "copied": "{{label}} 已复制", + "openPost": "打开帖子", + "noData": "暂无数据", + "page": "第 {{current}} / {{total}} 页", + "status": { + "label": "状态", + "pending": "待处理", + "approved": "已通过", + "rejected": "已拒绝" + } + } } } diff --git a/packages/common-i18n/src/locales/zh/sdk.json b/packages/common-i18n/src/locales/zh/sdk.json index 3f8ddafa70..0f9ce22b6d 100644 --- a/packages/common-i18n/src/locales/zh/sdk.json +++ b/packages/common-i18n/src/locales/zh/sdk.json @@ -1360,6 +1360,28 @@ "cannotDeployAppBeforeInitialization": "无法在初始化前部署应用", "noProjectOrVersionFound": "没有找到项目或版本", "noDeploymentUrlAvailable": "没有可用的部署 URL" + }, + "reward": { + "notFound": "奖励记录不存在", + "unsupportedSourceType": "不支持的奖励来源类型", + "maxClaimsReached": "您本周已达到最大奖励领取次数 (2 次)", + "verificationFailed": "验证失败:{{errors}}", + "alreadyClaimedThisWeek": "您本周已为此账户领取过奖励", + "invalidPostUrl": "帖子链接格式无效", + "postAlreadyUsed": "此帖子已被用于领取奖励", + "unsupportedPlatformUrl": "不支持的社交平台链接", + "unsupportedPlatform": "不支持的平台:{{platform}}", + "minCharCount": "帖子内容至少需要 {{count}} 个字符", + "minFollowerCount": "账户至少需要 {{count}} 位粉丝", + "mustMention": "帖子必须提及 {{mention}}", + "fetchTweetFailed": "获取 X 推文失败:{{error}}", + "tweetNotFound": "未找到 X 推文:{{postId}}", + "fetchUserFailed": "获取 X 用户失败:{{error}}", + "xUserNotFound": "未找到 X 用户:{{username}}", + "fetchLinkedInPostFailed": "获取 LinkedIn 帖子失败:{{error}}", + "linkedInPostNotFound": "未找到 LinkedIn 帖子:{{postId}}", + "linkedInAuthorNotFound": "未找到 LinkedIn 作者:{{postId}}", + "fetchLinkedInUserFailed": "获取 LinkedIn 用户失败:{{error}}" } } } diff --git a/packages/common-i18n/src/locales/zh/space.json b/packages/common-i18n/src/locales/zh/space.json index 3b1f08a952..d19a45fe15 100644 --- a/packages/common-i18n/src/locales/zh/space.json +++ b/packages/common-i18n/src/locales/zh/space.json @@ -45,6 +45,10 @@ "pin": "收藏", "empty": "您收藏的数据库和空间将显示在这里" }, + "tooltip": { + "noPermissionToCreateBase": "您没有权限创建数据库", + "creatingBase": "正在创建数据库..." + }, "tip": { "delete": "确定要删除 \"<0/>\" 空间吗?", "title": "提示", diff --git a/packages/common-i18n/src/locales/zh/system.json b/packages/common-i18n/src/locales/zh/system.json index f0c3e79a1a..b90c910ff3 100644 --- a/packages/common-i18n/src/locales/zh/system.json +++ b/packages/common-i18n/src/locales/zh/system.json @@ -1,16 +1,21 @@ { "notFound": { - "title": "404 - 找不到页面" + "title": "页面未找到", + "description": "您访问的链接可能已失效或已被移动。" }, "links": { - "backToHome": "返回主页" + "backToHome": "返回首页" }, "forbidden": { - "title": "403 - 禁止访问", - "description": "您没有权限访问此页面" + "title": "访问受限", + "description": "您需要权限才能访问此资源。\n请联系管理员。" }, "paymentRequired": { - "title": "402 - 需要升级订阅", - "description": "您当前的订阅计划不支持访问此功能,请升级您的订阅以继续使用" + "title": "解锁高级功能", + "description": "此功能仅在高级计划中可用。\n升级以扩展您的能力。" + }, + "error": { + "title": "出错了", + "description": "发生了意外错误,请稍后重试。" } } diff --git a/packages/common-i18n/src/locales/zh/table.json b/packages/common-i18n/src/locales/zh/table.json index 7b7b985536..7b04e8513c 100644 --- a/packages/common-i18n/src/locales/zh/table.json +++ b/packages/common-i18n/src/locales/zh/table.json @@ -93,13 +93,13 @@ "hide": "隐藏", "default": { "singleLineText": { - "title": "标题" + "title": "单行文本" }, "longText": { - "title": "描述" + "title": "长文本" }, "number": { - "title": "计数", + "title": "数字", "formatType": "格式类型", "currencySymbol": "货币符号", "defaultSymbol": "¥", @@ -109,7 +109,7 @@ "percentExample": "百分比 (20%)" }, "singleSelect": { - "title": "状态", + "title": "单选", "options": { "todo": "待开始", "inProgress": "进行中", @@ -117,7 +117,7 @@ } }, "multipleSelect": { - "title": "标签" + "title": "多选" }, "attachment": { "title": "附件" @@ -146,7 +146,7 @@ "noDisplay": "不显示" }, "autoNumber": { - "title": "自动编号" + "title": "自增数字" }, "createdTime": { "title": "创建时间" diff --git a/packages/icons/src/components/Coins.tsx b/packages/icons/src/components/Coins.tsx new file mode 100644 index 0000000000..5b1dbb2464 --- /dev/null +++ b/packages/icons/src/components/Coins.tsx @@ -0,0 +1,18 @@ +import * as React from 'react'; +import type { SVGProps } from 'react'; +const Coins = (props: SVGProps) => ( + + + +); +export default Coins; diff --git a/packages/icons/src/components/Credits.tsx b/packages/icons/src/components/Credits.tsx new file mode 100644 index 0000000000..d9158a3daa --- /dev/null +++ b/packages/icons/src/components/Credits.tsx @@ -0,0 +1,24 @@ +import * as React from 'react'; +import type { SVGProps } from 'react'; +const Credits = (props: SVGProps) => ( + + + + +); +export default Credits; diff --git a/packages/icons/src/components/GiftPerson.tsx b/packages/icons/src/components/GiftPerson.tsx new file mode 100644 index 0000000000..29cbbfece5 --- /dev/null +++ b/packages/icons/src/components/GiftPerson.tsx @@ -0,0 +1,141 @@ +import * as React from 'react'; +import type { SVGProps } from 'react'; +const GiftPerson = (props: SVGProps) => ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +); +export default GiftPerson; diff --git a/packages/icons/src/components/GiftPersonDark.tsx b/packages/icons/src/components/GiftPersonDark.tsx new file mode 100644 index 0000000000..4f81b54def --- /dev/null +++ b/packages/icons/src/components/GiftPersonDark.tsx @@ -0,0 +1,176 @@ +import * as React from 'react'; +import type { SVGProps } from 'react'; +const GiftPerson = (props: SVGProps) => ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +); +export default GiftPerson; diff --git a/packages/icons/src/components/LinkedIn.tsx b/packages/icons/src/components/LinkedIn.tsx new file mode 100644 index 0000000000..2b0d8a06be --- /dev/null +++ b/packages/icons/src/components/LinkedIn.tsx @@ -0,0 +1,32 @@ +import * as React from 'react'; +import type { SVGProps } from 'react'; + +interface LinkedInProps extends SVGProps { + bgFill?: string; + fgFill?: string; +} + +const LinkedIn = ({ bgFill = '#007EBB', fgFill = 'white', ...props }: LinkedInProps) => ( + + + + +); +export default LinkedIn; diff --git a/packages/icons/src/components/ShieldUser.tsx b/packages/icons/src/components/ShieldUser.tsx new file mode 100644 index 0000000000..30c57567d9 --- /dev/null +++ b/packages/icons/src/components/ShieldUser.tsx @@ -0,0 +1,18 @@ +import * as React from 'react'; +import type { SVGProps } from 'react'; +const ShieldUser = (props: SVGProps) => ( + + + +); +export default ShieldUser; diff --git a/packages/icons/src/components/Token.tsx b/packages/icons/src/components/Token.tsx new file mode 100644 index 0000000000..e29c0282cf --- /dev/null +++ b/packages/icons/src/components/Token.tsx @@ -0,0 +1,22 @@ +import * as React from 'react'; +import type { SVGProps } from 'react'; + +const Token = (props: SVGProps) => { + return ( + + + + ); +}; + +export default Token; diff --git a/packages/icons/src/index.ts b/packages/icons/src/index.ts index 02c82a403b..1cef5efa08 100644 --- a/packages/icons/src/index.ts +++ b/packages/icons/src/index.ts @@ -42,14 +42,16 @@ export { default as Clock4 } from './components/Clock4'; export { default as Code } from './components/Code'; export { default as Code2 } from './components/Code2'; export { default as Cohere } from './components/Cohere'; +export { default as Coins } from './components/Coins'; export { default as Component } from './components/Component'; export { default as Condition } from './components/Condition'; export { default as ConditionalLookup } from './components/ConditionalLookup'; export { default as ConditionalRollup } from './components/ConditionalRollup'; export { default as Copy } from './components/Copy'; -export { default as CreditCard } from './components/CreditCard'; export { default as Cuppy } from './components/Cuppy'; export { default as CuppyLoader } from './components/CuppyLoader'; +export { default as Credits } from './components/Credits'; +export { default as CreditCard } from './components/CreditCard'; export { default as Database } from './components/Database'; export { default as DeepThinking } from './components/DeepThinking'; export { default as Deepseek } from './components/Deepseek'; @@ -95,6 +97,8 @@ export { default as Flame } from './components/Flame'; export { default as FreezeColumn } from './components/FreezeColumn'; export { default as Frown } from './components/Frown'; export { default as Gauge } from './components/Gauge'; +export { default as GiftPerson } from './components/GiftPerson'; +export { default as GiftPersonDark } from './components/GiftPersonDark'; export { default as Github } from './components/Github'; export { default as GithubLogo } from './components/GithubLogo'; export { default as GoogleLogo } from './components/GoogleLogo'; @@ -115,6 +119,7 @@ export { default as LayoutGrid } from './components/LayoutGrid'; export { default as LayoutList } from './components/LayoutList'; export { default as LayoutTemplate } from './components/LayoutTemplate'; export { default as License } from './components/License'; +export { default as LinkedIn } from './components/LinkedIn'; export { default as Line1 } from './components/Line1'; export { default as Line2 } from './components/Line2'; export { default as Line3 } from './components/Line3'; @@ -164,6 +169,7 @@ export { default as Settings } from './components/Settings'; export { default as Share2 } from './components/Share2'; export { default as Sheet } from './components/Sheet'; export { default as ShieldCheck } from './components/ShieldCheck'; +export { default as ShieldUser } from './components/ShieldUser'; export { default as Slack } from './components/Slack'; export { default as Sidebar } from './components/Sidebar'; export { default as SortAsc } from './components/SortAsc'; @@ -176,6 +182,7 @@ export { default as Table2 } from './components/Table2'; export { default as Teable } from './components/Teable'; export { default as TeableNew } from './components/TeableNew'; export { default as ThumbsUp } from './components/ThumbsUp'; +export { default as Token } from './components/Token'; export { default as Translation } from './components/Translation'; export { default as Trash } from './components/Trash'; export { default as Trash2 } from './components/Trash2'; diff --git a/packages/openapi/src/admin/setting/get-public.ts b/packages/openapi/src/admin/setting/get-public.ts index f2fe274e0e..598b293c04 100644 --- a/packages/openapi/src/admin/setting/get-public.ts +++ b/packages/openapi/src/admin/setting/get-public.ts @@ -46,6 +46,7 @@ export const publicSettingVoSchema = settingVoSchema changeEmailSendCodeMailRate: z.number().optional(), resetPasswordSendMailRate: z.number().optional(), signupVerificationSendCodeMailRate: z.number().optional(), + enableCreditReward: z.boolean().optional(), }); export type IPublicSettingVo = z.infer; diff --git a/packages/openapi/src/admin/setting/get.ts b/packages/openapi/src/admin/setting/get.ts index 2f18435171..0f78abfa90 100644 --- a/packages/openapi/src/admin/setting/get.ts +++ b/packages/openapi/src/admin/setting/get.ts @@ -20,6 +20,7 @@ export const settingVoSchema = z.object({ automationMailTransportConfig: mailTransportConfigSchema.nullable().optional(), appConfig: appConfigSchema.nullable().optional(), webSearchConfig: webSearchConfigSchema.nullable().optional(), + enableCreditReward: z.boolean().optional(), createdTime: z.string().optional(), }); diff --git a/packages/openapi/src/admin/setting/key.enum.ts b/packages/openapi/src/admin/setting/key.enum.ts index bc8103cd3f..a30da47fc1 100644 --- a/packages/openapi/src/admin/setting/key.enum.ts +++ b/packages/openapi/src/admin/setting/key.enum.ts @@ -13,4 +13,5 @@ export enum SettingKey { WEB_SEARCH_CONFIG = 'webSearchConfig', NOTIFY_MAIL_TRANSPORT_CONFIG = 'notifyMailTransportConfig', AUTOMATION_MAIL_TRANSPORT_CONFIG = 'automationMailTransportConfig', + ENABLE_CREDIT_REWARD = 'enableCreditReward', } diff --git a/packages/openapi/src/admin/setting/update.ts b/packages/openapi/src/admin/setting/update.ts index abe0219f3e..107f017964 100644 --- a/packages/openapi/src/admin/setting/update.ts +++ b/packages/openapi/src/admin/setting/update.ts @@ -151,6 +151,7 @@ export const updateSettingRoSchema = z.object({ enableWaitlist: z.boolean().optional(), appConfig: appConfigSchema.optional(), webSearchConfig: webSearchConfigSchema.optional(), + enableCreditReward: z.boolean().optional(), brandName: z.string().optional(), }); diff --git a/packages/sdk/src/config/local-storage-keys.ts b/packages/sdk/src/config/local-storage-keys.ts index 00640245de..bef6ad416b 100644 --- a/packages/sdk/src/config/local-storage-keys.ts +++ b/packages/sdk/src/config/local-storage-keys.ts @@ -24,4 +24,6 @@ export enum LocalStorageKeys { ChatPanel = 'ls_chat_panel', Chat = 'ls_chat', BaseNodeTreeExpandedItems = 'ls_base_node_tree_expanded_items', + WinCreditTriggerVisible = 'ls_win_credit_trigger_visible', + Sidebar = 'ls_sidebar', } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7a4e01ce0b..179f523706 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -739,9 +739,6 @@ importers: '@teable/ui-lib': specifier: workspace:^ version: link:../../packages/ui-lib - '@types/canvas-confetti': - specifier: 1.9.0 - version: 1.9.0 allotment: specifier: 1.20.0 version: 1.20.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -947,6 +944,9 @@ importers: '@testing-library/user-event': specifier: 14.5.2 version: 14.5.2(@testing-library/dom@9.3.4) + '@types/canvas-confetti': + specifier: 1.9.0 + version: 1.9.0 '@types/cors': specifier: 2.8.17 version: 2.8.17 @@ -1278,7 +1278,7 @@ importers: version: 9.1.0(eslint@8.57.0) eslint-import-resolver-typescript: specifier: 3.6.1 - version: 3.6.1(@typescript-eslint/parser@7.3.1(eslint@8.57.0)(typescript@5.4.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0) + version: 3.6.1(@typescript-eslint/parser@7.3.1(eslint@8.57.0)(typescript@5.4.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.3.1(eslint@8.57.0)(typescript@5.4.3))(eslint@8.57.0))(eslint@8.57.0) eslint-plugin-import: specifier: 2.29.1 version: 2.29.1(@typescript-eslint/parser@7.3.1(eslint@8.57.0)(typescript@5.4.3))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0) @@ -1780,7 +1780,7 @@ importers: version: 5.4.3 vite-plugin-svgr: specifier: 4.2.0 - version: 4.2.0(rollup@4.28.1)(typescript@5.4.3)(vite@5.4.11(@types/node@22.18.0)(terser@5.37.0)) + version: 4.2.0(rollup@2.79.2)(typescript@5.4.3)(vite@5.4.11(@types/node@22.18.0)(terser@5.37.0)) vite-tsconfig-paths: specifier: 4.3.2 version: 4.3.2(typescript@5.4.3)(vite@5.4.11(@types/node@22.18.0)(terser@5.37.0)) @@ -23681,6 +23681,14 @@ snapshots: optionalDependencies: rollup: 2.78.0 + '@rollup/pluginutils@5.1.4(rollup@2.79.2)': + dependencies: + '@types/estree': 1.0.6 + estree-walker: 2.0.2 + picomatch: 4.0.2 + optionalDependencies: + rollup: 2.79.2 + '@rollup/pluginutils@5.1.4(rollup@4.28.1)': dependencies: '@types/estree': 1.0.6 @@ -29652,7 +29660,7 @@ snapshots: '@typescript-eslint/parser': 7.3.1(eslint@8.57.0)(typescript@5.4.3) eslint: 8.57.0 eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@7.3.1(eslint@8.57.0)(typescript@5.4.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0) + eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@7.3.1(eslint@8.57.0)(typescript@5.4.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.3.1(eslint@8.57.0)(typescript@5.4.3))(eslint@8.57.0))(eslint@8.57.0) eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.3.1(eslint@8.57.0)(typescript@5.4.3))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0) eslint-plugin-jsx-a11y: 6.8.0(eslint@8.57.0) eslint-plugin-react: 7.34.1(eslint@8.57.0) @@ -29675,7 +29683,7 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.3.1(eslint@8.57.0)(typescript@5.4.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0): + eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.3.1(eslint@8.57.0)(typescript@5.4.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.3.1(eslint@8.57.0)(typescript@5.4.3))(eslint@8.57.0))(eslint@8.57.0): dependencies: debug: 4.4.0 enhanced-resolve: 5.17.1 @@ -29720,7 +29728,7 @@ snapshots: '@typescript-eslint/parser': 7.3.1(eslint@8.57.0)(typescript@5.4.3) eslint: 8.57.0 eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@7.3.1(eslint@8.57.0)(typescript@5.4.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0) + eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@7.3.1(eslint@8.57.0)(typescript@5.4.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.3.1(eslint@8.57.0)(typescript@5.4.3))(eslint@8.57.0))(eslint@8.57.0) transitivePeerDependencies: - supports-color @@ -38292,9 +38300,9 @@ snapshots: - supports-color - typescript - vite-plugin-svgr@4.2.0(rollup@4.28.1)(typescript@5.4.3)(vite@5.4.11(@types/node@22.18.0)(terser@5.37.0)): + vite-plugin-svgr@4.2.0(rollup@2.79.2)(typescript@5.4.3)(vite@5.4.11(@types/node@22.18.0)(terser@5.37.0)): dependencies: - '@rollup/pluginutils': 5.1.4(rollup@4.28.1) + '@rollup/pluginutils': 5.1.4(rollup@2.79.2) '@svgr/core': 8.1.0(typescript@5.4.3) '@svgr/plugin-jsx': 8.1.0(@svgr/core@8.1.0(typescript@5.4.3)) vite: 5.4.11(@types/node@22.18.0)(terser@5.37.0)