From fa19fe63c994486bfd34df941fabf1eeac5361ba Mon Sep 17 00:00:00 2001 From: kumfo Date: Thu, 25 Jul 2024 16:40:04 +0800 Subject: [PATCH 01/64] fix(update): fix update to v1.3.6 --- internal/migrations/migrations.go | 1 + internal/migrations/v21.go | 32 +++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+) create mode 100644 internal/migrations/v21.go diff --git a/internal/migrations/migrations.go b/internal/migrations/migrations.go index 8a259684d..463f68ed8 100644 --- a/internal/migrations/migrations.go +++ b/internal/migrations/migrations.go @@ -96,6 +96,7 @@ var migrations = []Migration{ NewMigration("v1.2.1", "add password login control", addPasswordLoginControl, true), NewMigration("v1.2.5", "add notification plugin and theme config", addNotificationPluginAndThemeConfig, true), NewMigration("v1.3.0", "add review", addReview, false), + NewMigration("v1.3.6", "add hot score to question table", addQuestionHotScore, true), } func GetMigrations() []Migration { diff --git a/internal/migrations/v21.go b/internal/migrations/v21.go new file mode 100644 index 000000000..880852f8e --- /dev/null +++ b/internal/migrations/v21.go @@ -0,0 +1,32 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package migrations + +import ( + "context" + "xorm.io/xorm" +) + +func addQuestionHotScore(ctx context.Context, x *xorm.Engine) error { + type Question struct { + HotScore int `xorm:"not null default 0 INT(11) hot_score"` + } + return x.Context(ctx).Sync(new(Question)) +} From d39c12f8f13c6a22509c77949a103b4ca20e0ecb Mon Sep 17 00:00:00 2001 From: LinkinStars Date: Tue, 30 Jul 2024 10:47:12 +0800 Subject: [PATCH 02/64] docs(docs): remove the images about installation --- docs/img/install-database.png | Bin 13214 -> 0 bytes docs/img/install-site-info.png | Bin 43093 -> 0 bytes 2 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 docs/img/install-database.png delete mode 100644 docs/img/install-site-info.png diff --git a/docs/img/install-database.png b/docs/img/install-database.png deleted file mode 100644 index 09fbf36a18bb039cfaec997dd76fc6e4b9c12599..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13214 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 diff --git a/docs/img/install-site-info.png b/docs/img/install-site-info.png deleted file mode 100644 index b8166caf426bca8f51a475b8c23da10617761ed1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 43093 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 DY$>gM From 98a2de61f1a52a05547bfad48fd1c5a0c26434d4 Mon Sep 17 00:00:00 2001 From: Sonui Date: Wed, 31 Jul 2024 12:49:00 +0800 Subject: [PATCH 03/64] fix: Update Test_emailRepo_VerifyCode to handle additional parameters This commit updates the `Test_emailRepo_VerifyCode` function in the `email_repo_test.go` file. It adds support for additional parameters in the `SetCode` function call, specifically the `user_id` and `skip_validation_latest_code` parameters. This change ensures that the `VerifyCode` function can properly handle the updated code format. --- internal/repo/repo_test/email_repo_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/repo/repo_test/email_repo_test.go b/internal/repo/repo_test/email_repo_test.go index f9f433723..62d1ccb85 100644 --- a/internal/repo/repo_test/email_repo_test.go +++ b/internal/repo/repo_test/email_repo_test.go @@ -30,8 +30,8 @@ import ( func Test_emailRepo_VerifyCode(t *testing.T) { emailRepo := export.NewEmailRepo(testDataSource) - code, content := "1111", "test" - err := emailRepo.SetCode(context.TODO(), code, content, time.Minute) + code, content := "1111", "{\"source_type\":\"\",\"e_mail\":\"\",\"user_id\":\"1\",\"skip_validation_latest_code\":false}" + err := emailRepo.SetCode(context.TODO(), "1", code, content, time.Minute) assert.NoError(t, err) verifyContent, err := emailRepo.VerifyCode(context.TODO(), code) From 2167a8243c5660d68ac504cae03d6bfd1d6fb842 Mon Sep 17 00:00:00 2001 From: Sonui Date: Thu, 1 Aug 2024 23:53:55 +0800 Subject: [PATCH 04/64] fix: set last answer ID to zero if no answers --- internal/service/question_common/question.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/internal/service/question_common/question.go b/internal/service/question_common/question.go index fce3761ea..b9f2fd539 100644 --- a/internal/service/question_common/question.go +++ b/internal/service/question_common/question.go @@ -145,6 +145,15 @@ func (qs *QuestionCommon) UpdateAnswerCount(ctx context.Context, questionID stri if err != nil { return err } + if count == 0 { + err = qs.questionRepo.UpdateLastAnswer(ctx, &entity.Question{ + ID: questionID, + LastAnswerID: "0", + }) + if err != nil { + return err + } + } return qs.questionRepo.UpdateAnswerCount(ctx, questionID, int(count)) } From 8762f5b751a99da9e525b2f1c4625a5472813c4e Mon Sep 17 00:00:00 2001 From: Sonui Date: Fri, 2 Aug 2024 00:01:01 +0800 Subject: [PATCH 05/64] typo: GetCss swagger annotation content --- internal/controller_admin/siteinfo_controller.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/internal/controller_admin/siteinfo_controller.go b/internal/controller_admin/siteinfo_controller.go index 623ab5f79..6429d6719 100644 --- a/internal/controller_admin/siteinfo_controller.go +++ b/internal/controller_admin/siteinfo_controller.go @@ -188,12 +188,12 @@ func (sc *SiteInfoController) GetRobots(ctx *gin.Context) { ctx.String(http.StatusOK, resp.Robots) } -// GetRobots get site robots information -// @Summary get site robots information -// @Description get site robots information +// GetCss get site custom CSS +// @Summary get site custom CSS +// @Description get site custom CSS // @Tags site -// @Produce json -// @Success 200 {string} txt "" +// @Produce text/css +// @Success 200 {string} css "" // @Router /custom.css [get] func (sc *SiteInfoController) GetCss(ctx *gin.Context) { resp, err := sc.siteInfoService.GetSiteCustomCssHTML(ctx) From 5afa01ba8982ac91bac6e4d7ed399bd7e4fb98c8 Mon Sep 17 00:00:00 2001 From: Sonui Date: Fri, 2 Aug 2024 09:33:48 +0800 Subject: [PATCH 06/64] perf: optimize search for unanswered questions using answer_count --- internal/repo/question/question_repo.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/repo/question/question_repo.go b/internal/repo/question/question_repo.go index 5a683af73..83a841573 100644 --- a/internal/repo/question/question_repo.go +++ b/internal/repo/question/question_repo.go @@ -385,7 +385,7 @@ func (qr *questionRepo) GetQuestionPage(ctx context.Context, page, pageSize int, case "score": session.OrderBy("question.pin desc,question.vote_count DESC, question.view_count DESC") case "unanswered": - session.Where("question.last_answer_id = 0") + session.Where("question.answer_count = 0") session.OrderBy("question.pin desc,question.created_at DESC") } From 11fb3aafd879c001cc98d11214406140a02d0daf Mon Sep 17 00:00:00 2001 From: EkkoKo <65719025+EkkoKo@users.noreply.github.com> Date: Tue, 23 Jul 2024 12:58:06 +0300 Subject: [PATCH 07/64] Makefile: Wraped with quotes the go location in go variable --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 9cda2f5ed..9dc8c983e 100644 --- a/Makefile +++ b/Makefile @@ -8,7 +8,7 @@ DOCKER_CMD=docker GO_ENV=CGO_ENABLED=0 GO111MODULE=on Revision=$(shell git rev-parse --short HEAD 2>/dev/null || echo "") GO_FLAGS=-ldflags="-X github.com/apache/incubator-answer/cmd.Version=$(VERSION) -X 'github.com/apache/incubator-answer/cmd.Revision=$(Revision)' -X 'github.com/apache/incubator-answer/cmd.Time=`date +%s`' -extldflags -static" -GO=$(GO_ENV) $(shell which go) +GO=$(GO_ENV) "$(shell which go)" build: generate @$(GO) build $(GO_FLAGS) -o $(BIN) $(DIR_SRC) From c3a17046c6c3be1cec16ba49d07d9f7742b7260f Mon Sep 17 00:00:00 2001 From: shuai Date: Tue, 6 Aug 2024 16:43:13 +0800 Subject: [PATCH 08/64] fix: gavatar use sha256 for hash --- ui/package.json | 2 +- ui/pnpm-lock.yaml | 30 +++++-------------- ui/src/pages/Users/Settings/Profile/index.tsx | 4 +-- 3 files changed, 10 insertions(+), 26 deletions(-) diff --git a/ui/package.json b/ui/package.json index 19e669818..8a087428c 100644 --- a/ui/package.json +++ b/ui/package.json @@ -31,9 +31,9 @@ "diff": "^5.1.0", "front-matter": "^4.0.2", "i18next": "^21.9.0", + "js-sha256": "0.11.0", "lodash": "^4.17.21", "marked": "^4.0.19", - "md5": "^2.3.0", "next-share": "^0.18.1", "qrcode": "^1.5.1", "qs": "^6.11.0", diff --git a/ui/pnpm-lock.yaml b/ui/pnpm-lock.yaml index b855bfa05..77bbee2d4 100644 --- a/ui/pnpm-lock.yaml +++ b/ui/pnpm-lock.yaml @@ -53,15 +53,15 @@ importers: i18next: specifier: ^21.9.0 version: 21.9.2 + js-sha256: + specifier: 0.11.0 + version: 0.11.0 lodash: specifier: ^4.17.21 version: 4.17.21 marked: specifier: ^4.0.19 version: 4.1.0 - md5: - specifier: ^2.3.0 - version: 2.3.0 next-share: specifier: ^0.18.1 version: 0.18.1(react-dom@18.2.0)(react-scripts@5.0.1)(react@18.2.0) @@ -4642,10 +4642,6 @@ packages: resolution: {integrity: sha512-oSvEeo6ZUD7NepqAat3RqoucZ5SeqLJgOvVIwkafu6IP3V0pO38s/ypdVUmDDK6qIIHNlYHJAKX9E7R7HoKElw==} engines: {node: '>=12.20'} - /charenc@0.0.2: - resolution: {integrity: sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==} - dev: false - /check-types@11.1.2: resolution: {integrity: sha512-tzWzvgePgLORb9/3a0YenggReLKAIb2owL03H2Xdoe5pKcUyWRSEQ8xfCar8t2SIAuEDwtmx2da1YB52YuHQMQ==} @@ -4977,10 +4973,6 @@ packages: shebang-command: 2.0.0 which: 2.0.2 - /crypt@0.0.2: - resolution: {integrity: sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==} - dev: false - /crypto-random-string@2.0.0: resolution: {integrity: sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==} engines: {node: '>=8'} @@ -7207,10 +7199,6 @@ packages: call-bind: 1.0.5 has-tostringtag: 1.0.0 - /is-buffer@1.1.6: - resolution: {integrity: sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==} - dev: false - /is-callable@1.2.6: resolution: {integrity: sha512-krO72EO2NptOGAX2KYyqbP9vYMlNAXdB53rq6f8LXY6RY7JdSR/3BD6wLUlPHSAesmY9vstNrjvqGaCiRK/91Q==} engines: {node: '>= 0.4'} @@ -8017,6 +8005,10 @@ packages: - ts-node - utf-8-validate + /js-sha256@0.11.0: + resolution: {integrity: sha512-6xNlKayMZvds9h1Y1VWc0fQHQ82BxTXizWPEtEeGvmOUYpBRy4gbWroHLpzowe6xiQhHpelCQiE7HEdznyBL9Q==} + dev: false + /js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} @@ -8362,14 +8354,6 @@ packages: hasBin: true dev: false - /md5@2.3.0: - resolution: {integrity: sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==} - dependencies: - charenc: 0.0.2 - crypt: 0.0.2 - is-buffer: 1.1.6 - dev: false - /mdn-data@2.0.14: resolution: {integrity: sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==} diff --git a/ui/src/pages/Users/Settings/Profile/index.tsx b/ui/src/pages/Users/Settings/Profile/index.tsx index ab2ee0f1a..9af28ce6e 100644 --- a/ui/src/pages/Users/Settings/Profile/index.tsx +++ b/ui/src/pages/Users/Settings/Profile/index.tsx @@ -21,7 +21,7 @@ import React, { FormEvent, useState, useEffect } from 'react'; import { Form, Button, Stack, ButtonGroup } from 'react-bootstrap'; import { Trans, useTranslation } from 'react-i18next'; -import MD5 from 'md5'; +import { sha256 } from 'js-sha256'; import type { FormDataType } from '@/common/interface'; import { UploadImg, Avatar, Icon, ImgViewer } from '@/components'; @@ -273,7 +273,7 @@ const Index: React.FC = () => { setFormData({ ...formData }); if (res.e_mail) { const str = res.e_mail.toLowerCase().trim(); - const hash = MD5(str); + const hash = sha256(str); setMailHash(hash); } }); From 38834e6294b566e29fdeca2ed318cdce6bf3dc26 Mon Sep 17 00:00:00 2001 From: sy-records <52o@qq52o.cn> Date: Mon, 5 Aug 2024 17:03:23 +0800 Subject: [PATCH 09/64] feat: Add Open Search support --- internal/controller/template_controller.go | 8 +++++ .../controller/template_render/question.go | 22 ++++++++++++++ internal/router/template_router.go | 2 ++ ui/template/header.html | 1 + ui/template/opensearch.xml | 29 +++++++++++++++++++ 5 files changed, 62 insertions(+) create mode 100644 ui/template/opensearch.xml diff --git a/internal/controller/template_controller.go b/internal/controller/template_controller.go index a7d69ebc8..09f5bffcb 100644 --- a/internal/controller/template_controller.go +++ b/internal/controller/template_controller.go @@ -562,6 +562,14 @@ func (tc *TemplateController) html(ctx *gin.Context, code int, tpl string, siteI ctx.HTML(code, tpl, data) } +func (tc *TemplateController) OpenSearch(ctx *gin.Context) { + if tc.checkPrivateMode(ctx) { + tc.Page404(ctx) + return + } + tc.templateRenderController.OpenSearch(ctx) +} + func (tc *TemplateController) Sitemap(ctx *gin.Context) { if tc.checkPrivateMode(ctx) { tc.Page404(ctx) diff --git a/internal/controller/template_render/question.go b/internal/controller/template_render/question.go index a65c3083d..83d08dc72 100644 --- a/internal/controller/template_render/question.go +++ b/internal/controller/template_render/question.go @@ -89,6 +89,28 @@ func (t *TemplateRenderController) Sitemap(ctx *gin.Context) { ) } +func (t *TemplateRenderController) OpenSearch(ctx *gin.Context) { + general, err := t.siteInfoService.GetSiteGeneral(ctx) + if err != nil { + log.Error("get site general failed:", err) + return + } + + favicon := "favicon.ico" + branding, err := t.siteInfoService.GetSiteBranding(ctx) + if err == nil { + favicon = branding.Favicon + } + + ctx.Header("Content-Type", "application/xml") + ctx.HTML( + http.StatusOK, "opensearch.xml", gin.H{ + "general": general, + "favicon": favicon, + }, + ) +} + func (t *TemplateRenderController) SitemapPage(ctx *gin.Context, page int) error { general, err := t.siteInfoService.GetSiteGeneral(ctx) if err != nil { diff --git a/internal/router/template_router.go b/internal/router/template_router.go index 01e8b3073..195030f9a 100644 --- a/internal/router/template_router.go +++ b/internal/router/template_router.go @@ -60,6 +60,8 @@ func (a *TemplateRouter) RegisterTemplateRouter(r *gin.RouterGroup, baseURLPath seoNoAuth.GET("/404", a.templateController.Page404) + seoNoAuth.GET("/opensearch.xml", a.templateController.OpenSearch) + seo := r.Group(baseURLPath) seo.Use(a.authUserMiddleware.CheckPrivateMode()) seo.GET("/", a.templateController.Index) diff --git a/ui/template/header.html b/ui/template/header.html index 6eff73402..653f9a28a 100644 --- a/ui/template/header.html +++ b/ui/template/header.html @@ -33,6 +33,7 @@ + + + {{$.general.Name}} + {{if $.general.Description}} + {{$.general.Description}} + {{end}} + UTF-8 + {{$.favicon}} + + From 1b043d3a64fc6f7f9b30ee1e735bd7d37a4492ff Mon Sep 17 00:00:00 2001 From: sy-records <52o@qq52o.cn> Date: Tue, 6 Aug 2024 15:39:55 +0800 Subject: [PATCH 10/64] fix: branding favicon might be empty --- internal/controller/template_render/question.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/controller/template_render/question.go b/internal/controller/template_render/question.go index 83d08dc72..b97536188 100644 --- a/internal/controller/template_render/question.go +++ b/internal/controller/template_render/question.go @@ -96,9 +96,9 @@ func (t *TemplateRenderController) OpenSearch(ctx *gin.Context) { return } - favicon := "favicon.ico" + favicon := general.SiteUrl + "/favicon.ico" branding, err := t.siteInfoService.GetSiteBranding(ctx) - if err == nil { + if err == nil && len(branding.Favicon) > 0 { favicon = branding.Favicon } From 016abd1a9825ca710cee5b6df03ee5e27008e00e Mon Sep 17 00:00:00 2001 From: "James Roller, Jr." Date: Mon, 5 Aug 2024 20:10:12 -0700 Subject: [PATCH 11/64] add pvc dataSource - allow for restoring a volumeSnapshot --- charts/templates/pvc.yaml | 8 +++++++- charts/values.yaml | 8 +++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/charts/templates/pvc.yaml b/charts/templates/pvc.yaml index 5dfab8fca..640fb9fe0 100644 --- a/charts/templates/pvc.yaml +++ b/charts/templates/pvc.yaml @@ -20,7 +20,7 @@ kind: PersistentVolumeClaim apiVersion: v1 metadata: name: {{ include "answer.fullname" . }}-claim - {{- with .Values.persistence.annotations }} + {{- with .Values.persistence.annotations }} annotations: {{ toYaml . | indent 4 }} {{- end }} @@ -39,4 +39,10 @@ spec: resources: requests: storage: {{ .Values.persistence.size | quote }} + {{- with .Values.persistence.dataSource }} + dataSource: + name: {{ .name }} + kind: {{ .kind | default "VolumeSnapshot" }} + apiGroup: {{ .apiGroup | default "snapshot.storage.k8s.io" }} + {{- end }} {{- end }} \ No newline at end of file diff --git a/charts/values.yaml b/charts/values.yaml index 8bb36c46f..d932db848 100644 --- a/charts/values.yaml +++ b/charts/values.yaml @@ -68,7 +68,7 @@ env: # Configure extra containers extraContainers: [] - # - name: cloudsql-proxy + # - name: cloudsql-proxy # image: gcr.io/cloud-sql-connectors/cloud-sql-proxy:2.1.2 # command: # - /cloud-sql-proxy @@ -91,6 +91,12 @@ persistence: accessMode: ReadWriteOnce size: 5Gi annotations: {} + # To restore a PVC from a VolumeSnapshot, set the dataSource; + # the kind and apiGroup are optional and default to the shown values + dataSource: {} + # name: my-volume-snapshot + # kind: VolumeSnapshot + # apiGroup: snapshot.storage.k8s.io imagePullSecrets: [] nameOverride: "" From eda39380758e838a6c294d2a52ce1c325ebb2cd3 Mon Sep 17 00:00:00 2001 From: Sonui Date: Wed, 7 Aug 2024 22:51:23 +0800 Subject: [PATCH 12/64] perf: remove invalid nil pointer check in Translate function --- plugin/plugin.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin/plugin.go b/plugin/plugin.go index 61ed0d8f7..852868557 100644 --- a/plugin/plugin.go +++ b/plugin/plugin.go @@ -219,7 +219,7 @@ func MakeTranslator(key string) Translator { // Translate translates the key to the current language of the context func (t Translator) Translate(ctx *GinContext) string { - if &t == nil || t.Fn == nil { + if t.Fn == nil { return "" } return t.Fn(ctx) From 4b3e0eaa4e407842d62ec19b4313942b53d3809e Mon Sep 17 00:00:00 2001 From: Sonui Date: Wed, 14 Aug 2024 00:21:55 +0800 Subject: [PATCH 13/64] perf: update check-asf-header.sh script to support Podman --- script/check-asf-header.sh | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/script/check-asf-header.sh b/script/check-asf-header.sh index ff765eab5..808efa108 100755 --- a/script/check-asf-header.sh +++ b/script/check-asf-header.sh @@ -16,5 +16,16 @@ # specific language governing permissions and limitations # under the License. -docker run -it --rm -v $(pwd):/github/workspace ghcr.io/korandoru/hawkeye-native format +# check if docker or podman is installed +if command -v docker >/dev/null 2>&1; then + CONTAINER_RUNTIME="docker" +elif command -v podman >/dev/null 2>&1; then + CONTAINER_RUNTIME="podman" +else + echo "Neither Docker nor Podman is installed. Please install either Docker or Podman." + exit 1 +fi + +$CONTAINER_RUNTIME run -it --rm -v "$(pwd)":/github/workspace ghcr.io/korandoru/hawkeye-native format + gofmt -w -l . From 969169243bac95cc15eeee53e5dd4ebb307c37b1 Mon Sep 17 00:00:00 2001 From: Sonui Date: Thu, 15 Aug 2024 21:15:02 +0800 Subject: [PATCH 14/64] perf: remove extra characters in header.html --- ui/template/header.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/template/header.html b/ui/template/header.html index 653f9a28a..0243a43d7 100644 --- a/ui/template/header.html +++ b/ui/template/header.html @@ -68,14 +68,14 @@ From b41bafad30587ccada6818b58dd3328f22876aa4 Mon Sep 17 00:00:00 2001 From: kumfo Date: Mon, 19 Aug 2024 15:15:07 +0800 Subject: [PATCH 15/64] fix(dev): delete test file --- dev/i18n/zh_CN.yaml | 2035 ------------------------------------------- 1 file changed, 2035 deletions(-) delete mode 100644 dev/i18n/zh_CN.yaml diff --git a/dev/i18n/zh_CN.yaml b/dev/i18n/zh_CN.yaml deleted file mode 100644 index 6613bfc22..000000000 --- a/dev/i18n/zh_CN.yaml +++ /dev/null @@ -1,2035 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -# The following fields are used for back-end -backend: - base: - success: - other: 成功。 - unknown: - other: 未知错误。 - request_format_error: - other: 请求格式错误。 - unauthorized_error: - other: 未授权。 - database_error: - other: 数据服务器错误。 - forbidden_error: - other: 禁止访问。 - duplicate_request_error: - other: 重复提交。 - action: - report: - other: 举报 - edit: - other: 编辑 - delete: - other: 删除 - close: - other: 关闭 - reopen: - other: 重新打开 - forbidden_error: - other: 禁止访问。 - pin: - other: 置顶 - hide: - other: 列表隐藏 - unpin: - other: 取消置顶 - show: - other: 列表显示 - invite_someone_to_answer: - other: 编辑 - undelete: - other: 撤消删除 - role: - name: - user: - other: 用户 - admin: - other: 管理员 - moderator: - other: 版主 - description: - user: - other: 默认没有特殊权限。 - admin: - other: 拥有管理网站的全部权限。 - moderator: - other: 拥有除访问后台管理以外的所有权限。 - privilege: - level_1: - description: - other: 级别 1(少量声望要求,适合私有团队、群组) - level_2: - description: - other: 级别 2(低声望要求,适合初启动的社区) - level_3: - description: - other: 级别 3(高声望要求,适合成熟的社区) - level_custom: - description: - other: 自定义等级 - rank_question_add_label: - other: 提问 - rank_answer_add_label: - other: 写答案 - rank_comment_add_label: - other: 写评论 - rank_report_add_label: - other: 举报 - rank_comment_vote_up_label: - other: 点赞评论 - rank_link_url_limit_label: - other: 每次发布超过 2 个链接 - rank_question_vote_up_label: - other: 点赞问题 - rank_answer_vote_up_label: - other: 点赞答案 - rank_question_vote_down_label: - other: 点踩问题 - rank_answer_vote_down_label: - other: 点踩答案 - rank_invite_someone_to_answer_label: - other: 邀请回答 - rank_tag_add_label: - other: 创建新标签 - rank_tag_edit_label: - other: 编辑标签描述(需要审核) - rank_question_edit_label: - other: 编辑别人的问题(需要审核) - rank_answer_edit_label: - other: 编辑别人的答案(需要审核) - rank_question_edit_without_review_label: - other: 编辑别人的问题无需审核 - rank_answer_edit_without_review_label: - other: 编辑别人的答案无需审核 - rank_question_audit_label: - other: 审核问题编辑 - rank_answer_audit_label: - other: 审核回答编辑 - rank_tag_audit_label: - other: 审核标签编辑 - rank_tag_edit_without_review_label: - other: 编辑标签描述无需审核 - rank_tag_synonym_label: - other: 管理标签同义词 - email: - other: 邮箱 - e_mail: - other: 邮箱 - password: - other: 密码 - pass: - other: 密码 - original_text: - other: 本帖 - email_or_password_wrong_error: - other: 邮箱和密码不匹配。 - error: - common: - invalid_url: - other: 无效的 URL。 - password: - space_invalid: - other: 密码不得含有空格。 - admin: - cannot_update_their_password: - other: 你无法修改自己的密码。 - cannot_edit_their_profile: - other: 您不能修改您的个人资料。 - cannot_modify_self_status: - other: 你无法修改自己的状态。 - email_or_password_wrong: - other: 邮箱和密码不匹配。 - answer: - not_found: - other: 没有找到答案。 - cannot_deleted: - other: 没有删除权限。 - cannot_update: - other: 没有更新权限。 - question_closed_cannot_add: - other: 问题已关闭,无法添加。 - comment: - edit_without_permission: - other: 不允许编辑评论。 - not_found: - other: 评论未找到。 - cannot_edit_after_deadline: - other: 评论时间太久,无法修改。 - email: - duplicate: - other: 邮箱已存在。 - need_to_be_verified: - other: 邮箱需要验证。 - verify_url_expired: - other: 邮箱验证的网址已过期,请重新发送邮件。 - illegal_email_domain_error: - other: 此邮箱不在允许注册的邮箱域中。请使用其他邮箱尝试。 - lang: - not_found: - other: 语言文件未找到。 - object: - captcha_verification_failed: - other: 验证码错误。 - disallow_follow: - other: 你不能关注。 - disallow_vote: - other: 你不能投票。 - disallow_vote_your_self: - other: 你不能为自己的帖子投票。 - not_found: - other: 对象未找到。 - verification_failed: - other: 验证失败。 - email_or_password_incorrect: - other: 邮箱和密码不匹配。 - old_password_verification_failed: - other: 旧密码验证失败。 - new_password_same_as_previous_setting: - other: 新密码和旧密码相同。 - already_deleted: - other: 该帖子已被删除。 - meta: - object_not_found: - other: Meta 对象未找到 - question: - already_deleted: - other: 该帖子已被删除。 - under_review: - other: 您的帖子正在等待审核。它将在它获得批准后可见。 - not_found: - other: 问题未找到。 - cannot_deleted: - other: 没有删除权限。 - cannot_close: - other: 没有关闭权限。 - cannot_update: - other: 没有更新权限。 - rank: - fail_to_meet_the_condition: - other: 声望值未达到要求。 - vote_fail_to_meet_the_condition: - other: 感谢投票。你至少需要 {{.Rank}} 声望才能投票。 - no_enough_rank_to_operate: - other: 你至少需要 {{.Rank}} 声望才能执行此操作。 - report: - handle_failed: - other: 报告处理失败。 - not_found: - other: 报告未找到。 - tag: - already_exist: - other: 标签已存在。 - not_found: - other: 标签未找到。 - recommend_tag_not_found: - other: 推荐标签不存在。 - recommend_tag_enter: - other: 请选择至少一个必选标签。 - not_contain_synonym_tags: - other: 不应包含同义词标签。 - cannot_update: - other: 没有更新权限。 - is_used_cannot_delete: - other: 你不能删除这个正在使用的标签。 - cannot_set_synonym_as_itself: - other: 你不能将当前标签设为自己的同义词。 - smtp: - config_from_name_cannot_be_email: - other: 发件人名称不能是邮箱地址。 - theme: - not_found: - other: 主题未找到。 - revision: - review_underway: - other: 目前无法编辑,有一个版本在审阅队列中。 - no_permission: - other: 无权限修改。 - user: - external_login_missing_user_id: - other: 第三方平台没有提供唯一的 UserID,所以你不能登录,请联系网站管理员。 - external_login_unbinding_forbidden: - other: 请在移除此登录之前为你的账户设置登录密码。 - email_or_password_wrong: - other: - other: 邮箱和密码不匹配。 - not_found: - other: 用户未找到。 - suspended: - other: 用户已被封禁。 - username_invalid: - other: 用户名无效。 - username_duplicate: - other: 用户名已被使用。 - set_avatar: - other: 头像设置错误。 - cannot_update_your_role: - other: 你不能修改自己的角色。 - not_allowed_registration: - other: 该网站暂未开放注册。 - not_allowed_login_via_password: - other: 该网站暂不支持密码登录。 - access_denied: - other: 拒绝访问 - page_access_denied: - other: 您没有权限访问此页面。 - add_bulk_users_format_error: - other: "发生错误,{{.Field}} 格式错误,在 '{{.Content}}' 行数 {{.Line}}. {{.ExtraMessage}}" - add_bulk_users_amount_error: - other: "一次性添加的用户数量应在 1-{{.MaxAmount}} 之间。" - config: - read_config_failed: - other: 读取配置失败 - database: - connection_failed: - other: 数据库连接失败 - create_table_failed: - other: 创建表失败 - install: - create_config_failed: - other: 无法创建 config.yaml 文件。 - upload: - unsupported_file_format: - other: 不支持的文件格式。 - site_info: - config_not_found: - other: 未找到网站的该配置信息。 - reason: - spam: - name: - other: 垃圾信息 - desc: - other: 这个帖子是一个广告,或是破坏性行为。它对当前的主题无帮助或无关。 - rude_or_abusive: - name: - other: 粗鲁或辱骂的 - desc: - other: - - 一个有理智的人都会认为这种内容不适合进行尊重性的讨论。 - - 论坛 - a_duplicate: - name: - other: 重复内容 - desc: - other: 该问题有人问过,而且已经有了答案。 - placeholder: - other: 输入已有的问题链接 - not_a_answer: - name: - other: 不是答案 - desc: - other: - - 这张贴作为答案,但它不会试图回答 - - 这可能是一个编辑、一个评论、另一个问题。 - - 或全部删除。 - no_longer_needed: - name: - other: 不再需要 - desc: - other: 该评论已过时,对话性质或与此帖子无关。 - something: - name: - other: 其他原因 - desc: - other: 此帖子需要工作人员注意,因为是上述所列以外的其他理由。 - placeholder: - other: 让我们具体知道你关心的什么 - community_specific: - name: - other: 社区特定原因 - desc: - other: 该问题不符合社区准则。 - not_clarity: - name: - other: 需要细节或澄清 - desc: - other: 该问题目前涵盖多个问题。它应该侧重在一个问题上。 - looks_ok: - name: - other: 看起来没问题 - desc: - other: 这个帖子是好的,不是低质量。 - needs_edit: - name: - other: 需要编辑,我已做了修改。 - desc: - other: 改进和纠正你自己帖子中的问题。 - needs_close: - name: - other: 需要关闭 - desc: - other: 关闭的问题不能回答,但仍然可以编辑、投票和评论。 - needs_delete: - name: - other: 需要删除 - desc: - other: 该帖子将被删除。 - question: - close: - duplicate: - name: - other: 垃圾信息 - desc: - other: 此问题以前就有人问过,而且已经有了答案。 - guideline: - name: - other: 社区特定原因 - desc: - other: 该问题不符合社区准则。 - multiple: - name: - other: 需要细节或澄清 - desc: - other: - - 该问题目前涵盖多个问题。它应该侧重在一个问题上。 - - 只关注一个问题。 - other: - name: - other: 其他原因 - desc: - other: 该帖子存在上面没有列出的另一个原因。 - operation_type: - asked: - other: 提问于 - answered: - other: 回答于 - modified: - other: 修改于 - deleted_title: - other: 删除的问题 - notification: - action: - update_question: - other: 更新了问题 - answer_the_question: - other: 回答了问题 - update_answer: - other: 更新了答案 - accept_answer: - other: 采纳了答案 - comment_question: - other: 评论了问题 - comment_answer: - other: 评论了答案 - reply_to_you: - other: 回复了你 - mention_you: - other: 提到了你 - your_question_is_closed: - other: 你的问题已被关闭 - your_question_was_deleted: - other: 你的问题已被删除 - your_answer_was_deleted: - other: 你的答案已被删除 - your_comment_was_deleted: - other: 你的评论已被删除 - up_voted_question: - other: 点赞问题 - down_voted_question: - other: 点踩问题 - up_voted_answer: - other: 点赞答案 - down_voted_answer: - other: 点踩回答 - up_voted_comment: - other: 点赞评论 - invited_you_to_answer: - other: 邀请你回答 - email_tpl: - change_email: - title: - other: "[{{.SiteName}}] 确认你的新邮箱地址" - body: - other: "请点击以下链接确认你在 {{.SiteName}} 上的新邮箱地址:
\n{{.ChangeEmailUrl}}

\n\n如果你没有请求此更改,请忽略此邮件。\n" - new_answer: - title: - other: "[{{.SiteName}}] {{.DisplayName}} 回答了你的问题" - body: - other: "{{.QuestionTitle}}

\n\n{{.DisplayName}}:
\n
{{.AnswerSummary}}

\n在 {{.SiteName}} 上查看

\n\n--
\n取消订阅" - invited_you_to_answer: - title: - other: "[{{.SiteName}}] {{.DisplayName}} 邀请您回答问题" - body: - other: "{{.QuestionTitle}}

\n\n{{.DisplayName}}:
\n
我想你可能知道答案。

\n在 {{.SiteName}} 上查看

\n\n--
\n取消订阅" - new_comment: - title: - other: "[{{.SiteName}}] {{.DisplayName}} 评论了你的帖子" - body: - other: "{{.QuestionTitle}}

\n\n{{.DisplayName}}:
\n
{{.CommentSummary}}

\n在 {{.SiteName}} 上查看

\n\n--
\n取消订阅" - new_question: - title: - other: "[{{.SiteName}}] 新问题: {{.QuestionTitle}}" - body: - other: "{{.QuestionTitle}}
\n{{.Tags}}

\n\n--
\n取消订阅" - pass_reset: - title: - other: "[{{.SiteName }}] 重置密码" - body: - other: "有人要求在 [{{.SiteName}}] 上重置你的密码。

\n\n如果这不是你的操作,请安心忽略此电子邮件。

\n\n请点击以下链接设置一个新密码:
\n{{.PassResetUrl}}\n" - register: - title: - other: "[{{.SiteName}}] 确认你的新账户" - body: - other: "欢迎加入 {{.SiteName}}!

\n\n请点击以下链接确认并激活你的新账户:
\n{{.RegisterUrl}}

\n\n如果上面的链接不能点击,请将其复制并粘贴到你的浏览器地址栏中。\n" - test: - title: - other: "[{{.SiteName}}] 测试邮件" - body: - other: "这是一封测试邮件。" - action_activity_type: - upvote: - other: 点赞 - upvoted: - other: 点赞 - downvote: - other: 点踩 - downvoted: - other: 点踩 - accept: - other: 采纳 - accepted: - other: 已采纳 - edit: - other: 编辑 - review: - queued_post: - other: 排队的帖子 - flagged_post: - other: 举报的帖子 - suggested_post_edit: - other: 建议的编辑 - reaction: - tooltip: - other: "{{ .Names }} 以及另外 {{ .Count }} 个..." -plugin: - s3_cdn: - backend: - info: - name: - other: S3 存储 CDN - description: - other: 上传文件到S3存储 - config: - endpoint: - title: - other: Endpoint - description: - other: S3存储的Endpoint - bucket_name: - title: - other: Bucket名称 - description: - other: S3存储的Bucket名称 - object_key_prefix: - title: - other: 对象Key前缀 - description: - other: 对象键的前缀,如'answer/data/',以'/'结尾 - access_key_id: - title: - other: AccessKeyID - description: - other: S3存储的AccessKeyID - access_key_secret: - title: - other: AccessKeySecret - description: - other: S3存储的AccessKeySecret - access_token: - title: - other: AccessToken - description: - other: 访问 S3 所需的 AccessToken - visit_url_prefix: - title: - other: 访问URL前缀 - description: - other: 上传静态文件CDN最终访问地址的前缀,以 '/' 结尾 https://static.example.com/xxx/ - max_file_size: - title: - other: 文件最大大小(MB) - description: - other: 限制上传文件的最大大小,单位MB,默认为10MB - region: - title: - other: 区域(Region) - description: - other: S3存储区域 - disable_ssl: - title: - other: 禁用SSL - description: - other: 我们建议您使用SSL访问S3存储。如果您想禁用SSL,请选中此选项。 - err: - mis_storage_config: - other: 错误的存储配置导致上传失败 - file_not_found: - other: 文件未找到 - unsupported_file_type: - other: 不支持的文件类型 - over_file_size_limit: - other: 超过文件大小限制 - upload_file_failed: - other: 上传文件失败 - aliyunoss_cdn: - backend: - info: - name: - other: 阿里云CDN OSS存储 - description: - other: 上传文件到阿里云CDN OSS存储 - config: - endpoint: - title: - other: Endpoint - description: - other: 阿里云OSS存储的Endpoint - bucket_name: - title: - other: Bucket名称 - description: - other: 阿里云OSS存储的Bucket名称 - object_key_prefix: - title: - other: 对象Key前缀 - description: - other: 对象键的前缀,如'answer/data/',以'/'结尾 - access_key_id: - title: - other: AccessKeyID - description: - other: 阿里云OSS存储的AccessKeyID - access_key_secret: - title: - other: AccessKeySecret - description: - other: 阿里云OSS存储的AccessKeySecret - visit_url_prefix: - title: - other: 访问URL前缀 - description: - other: CDN最终访问地址的前缀,以 '/' 结尾 https://example.com/xxx/ - max_file_size: - title: - other: 最大文件大小(MB) - description: - other: 限制上传文件的最大大小,单位为MB,默认为 10MB - err: - mis_storage_config: - other: 错误的存储配置导致上传失败 - file_not_found: - other: 文件未找到 - unsupported_file_type: - other: 不支持的文件类型 - over_file_size_limit: - other: 超过文件大小限制 - upload_file_failed: - other: 上传文件失败 -# The following fields are used for interface presentation(Front-end) -ui: - how_to_format: - title: 如何排版 - desc: >- -
  • 添加链接

    <https://url.com>

    [标题](https://url.com)
  • 段落之间使用空行分隔

  • _斜体_ 或者 **粗体**

  • 使用 4 个空格缩进代码

  • 在行首添加 > 表示引用

  • 反引号进行转义 `像 _这样_`

  • 使用 ``` 创建代码块

    ```
    这是代码块
    ```
- pagination: - prev: 上一页 - next: 下一页 - page_title: - question: 问题 - questions: 问题 - tag: 标签 - tags: 标签 - tag_wiki: 标签维基 - create_tag: 创建标签 - edit_tag: 编辑标签 - ask_a_question: 提问题 - edit_question: 编辑问题 - edit_answer: 编辑回答 - search: 搜索 - posts_containing: 帖子包含 - settings: 设置 - notifications: 通知 - login: 登录 - sign_up: 注册 - account_recovery: 账号恢复 - account_activation: 账号激活 - confirm_email: 确认电子邮件 - account_suspended: 账号已被封禁 - admin: 后台管理 - change_email: 修改邮箱 - install: Answer 安装 - upgrade: Answer 升级 - maintenance: 网站维护 - users: 用户 - oauth_callback: 处理中 - http_404: HTTP 错误 404 - http_50X: HTTP 错误 500 - http_403: HTTP 错误 403 - logout: 退出 - notifications: - title: 通知 - inbox: 收件箱 - achievement: 成就 - new_alerts: 新通知 - all_read: 全部标记为已读 - show_more: 显示更多 - someone: 有人 - inbox_type: - all: 全部 - posts: 帖子 - invites: 邀请 - votes: 投票 - suspended: - title: 你的账号账号已被封禁 - until_time: "你的账号被封禁直到 {{ time }}。" - forever: 你的账号已被永久封禁。 - end: 你违反了我们的社区准则。 - contact_us: 联系我们 - editor: - blockquote: - text: 引用 - bold: - text: 粗体 - chart: - text: 图表 - flow_chart: 流程图 - sequence_diagram: 时序图 - class_diagram: 类图 - state_diagram: 状态图 - entity_relationship_diagram: 实体关系图 - user_defined_diagram: 用户自定义图表 - gantt_chart: 甘特图 - pie_chart: 饼图 - code: - text: 代码块 - add_code: 添加代码块 - form: - fields: - code: - label: 代码块 - msg: - empty: 代码块不能为空 - language: - label: 语言 - placeholder: 自动识别 - btn_cancel: 取消 - btn_confirm: 添加 - formula: - text: 公式 - options: - inline: 行内公式 - block: 块级公式 - heading: - text: 标题 - options: - h1: 标题 1 - h2: 标题 2 - h3: 标题 3 - h4: 标题 4 - h5: 标题 5 - h6: 标题 6 - help: - text: 帮助 - hr: - text: 水平线 - image: - text: 图片 - add_image: 添加图片 - tab_image: 上传图片 - form_image: - fields: - file: - label: 图像文件 - btn: 选择图片 - msg: - empty: 请选择图片文件。 - only_image: 只能上传图片文件。 - max_size: 文件大小不能超过 4 MB。 - desc: - label: 描述 - tab_url: 图片地址 - form_url: - fields: - url: - label: 图片地址 - msg: - empty: 图片地址不能为空 - name: - label: 描述 - btn_cancel: 取消 - btn_confirm: 添加 - uploading: 上传中 - indent: - text: 缩进 - outdent: - text: 减少缩进 - italic: - text: 斜体 - link: - text: 超链接 - add_link: 添加超链接 - form: - fields: - url: - label: 链接 - msg: - empty: 链接不能为空。 - name: - label: 描述 - btn_cancel: 取消 - btn_confirm: 添加 - ordered_list: - text: 有序列表 - unordered_list: - text: 无序列表 - table: - text: 表格 - heading: 表头 - cell: 单元格 - close_modal: - title: 关闭原因是... - btn_cancel: 取消 - btn_submit: 提交 - remark: - empty: 不能为空。 - msg: - empty: 请选择一个原因。 - report_modal: - flag_title: 我举报这篇帖子的原因是... - close_title: 我关闭这篇帖子的原因是... - review_question_title: 审查问题 - review_answer_title: 审查回答 - review_comment_title: 审查评论 - btn_cancel: 取消 - btn_submit: 提交 - remark: - empty: 不能为空 - msg: - empty: 请选择一个原因。 - not_a_url: URL 格式不正确。 - url_not_match: URL 来源与当前网站不匹配。 - tag_modal: - title: 创建新标签 - form: - fields: - display_name: - label: 显示名称 - msg: - empty: 显示名称不能为空。 - range: 显示名称不能超过 35 个字符。 - slug_name: - label: URL 固定链接 - desc: URL 固定链接不能超过 35 个字符。 - msg: - empty: URL 固定链接不能为空。 - range: URL 固定链接不能超过 35 个字符。 - character: URL 固定链接包含非法字符。 - desc: - label: 描述 - revision: - label: 编辑历史 - edit_summary: - label: 编辑备注 - placeholder: >- - 简单描述更改原因(更正拼写、修复语法、改进格式) - btn_cancel: 取消 - btn_submit: 提交 - btn_post: 发布新标签 - tag_info: - created_at: 创建于 - edited_at: 编辑于 - history: 历史 - synonyms: - title: 同义词 - text: 以下标签将被重置到 - empty: 此标签目前没有同义词。 - btn_add: 添加同义词 - btn_edit: 编辑 - btn_save: 保存 - synonyms_text: 以下标签将被重置到 - delete: - title: 删除标签 - tip_with_posts: >- -

我们不允许 删除带有帖子的标签

请先从帖子中移除此标签。

- tip_with_synonyms: >- -

我们不允许 删除带有同义词的标签

请先从此标签中删除同义词。

- tip: 确定要删除吗? - close: 关闭 - edit_tag: - title: 编辑标签 - default_reason: 编辑标签 - default_first_reason: 添加标签 - btn_save_edits: 保存更改 - btn_cancel: 取消 - dates: - long_date: MM 月 DD 日 - long_date_with_year: "YYYY 年 MM 月 DD 日" - long_date_with_time: "YYYY 年 MM 月 DD 日 HH:mm" - now: 刚刚 - x_seconds_ago: "{{count}} 秒前" - x_minutes_ago: "{{count}} 分钟前" - x_hours_ago: "{{count}} 小时前" - hour: 小时 - day: 天 - hours: 小时 - days: 日 - reaction: - heart: 爱心 - smile: 微笑 - frown: 愁 - btn_label: 添加或删除回应。 - undo_emoji: 撤销 {{ emoji }} 回应 - react_emoji: 用 {{ emoji }} 回应 - unreact_emoji: 撤销 {{ emoji }} - comment: - btn_add_comment: 添加评论 - reply_to: 回复 - btn_reply: 回复 - btn_edit: 编辑 - btn_delete: 删除 - btn_flag: 举报 - btn_save_edits: 保存更改 - btn_cancel: 取消 - show_more: "{{count}} 条剩余评论" - tip_question: >- - 使用评论提问更多信息或者提出改进意见。避免在评论里回答问题。 - tip_answer: >- - 使用评论对回答者进行回复,或者通知回答者你已更新了问题的内容。如果要补充或者完善问题的内容,请在原问题中更改。 - tip_vote: 它给帖子添加了一些有用的内容 - edit_answer: - title: 编辑回答 - default_reason: 编辑回答 - default_first_reason: 添加答案 - form: - fields: - revision: - label: 编辑历史 - answer: - label: 回答内容 - feedback: - characters: 内容长度至少 6 个字符 - edit_summary: - label: 编辑摘要 - placeholder: >- - 简单描述更改原因(更正拼写、修复语法、改进格式) - btn_save_edits: 保存更改 - btn_cancel: 取消 - tags: - title: 标签 - sort_buttons: - popular: 热门 - name: 名称 - newest: 最新 - button_follow: 关注 - button_following: 已关注 - tag_label: 个问题 - search_placeholder: 通过标签名称过滤 - no_desc: 此标签无描述。 - more: 更多 - ask: - title: 新增问题 - edit_title: 编辑问题 - default_reason: 编辑问题 - default_first_reason: 新增问题 - similar_questions: 相似问题 - form: - fields: - revision: - label: 修订版本 - title: - label: 标题 - placeholder: 请详细描述你的问题,想象你在问一个人 - msg: - empty: 标题不能为空。 - range: 标题最多 150 个字符 - body: - label: 内容 - msg: - empty: 内容不能为空。 - tags: - label: 标签 - msg: - empty: 必须选择一个标签 - answer: - label: 回答内容 - msg: - empty: 回答内容不能为空 - edit_summary: - label: 编辑备注 - placeholder: >- - 简单描述更改原因(更正拼写、修复语法、改进格式) - btn_post_question: 提交问题 - btn_save_edits: 保存更改 - answer_question: 回答自己的问题 - post_question&answer: 提交问题和回答 - tag_selector: - add_btn: 添加标签 - create_btn: 创建新标签 - search_tag: 搜索标签 - hint: "描述您的问题是关于什么,至少需要一个标签。" - no_result: 没有匹配的标签 - tag_required_text: 必选标签(至少一个) - header: - nav: - question: 问题 - tag: 标签 - user: 用户 - profile: 用户主页 - setting: 账号设置 - logout: 退出 - admin: 后台管理 - review: 审查 - bookmark: 收藏夹 - moderation: 管理 - search: - placeholder: 搜索 - footer: - build_on: >- - 由 <1>Apache Answer 提供动力 - 驱动问答社区的开源软件。
用爱制造 © {{cc}}. - upload_img: - name: 更改 - loading: 加载中... - pic_auth_code: - title: 验证码 - placeholder: 输入图片中的文字 - msg: - empty: 验证码不能为空。 - inactive: - first: >- - 就差一步!我们发送了一封激活邮件到 {{mail}}。请按照邮件中的说明激活你的账户。 - info: "如果没有收到,请检查你的垃圾邮件文件夹。" - another: >- - 我们向你的邮箱 {{mail}} 发送了另一封激活电子邮件。可能需要几分钟才能到达;请务必检查您的垃圾邮件箱。 - btn_name: 重新发送激活邮件 - change_btn_name: 更改邮箱 - msg: - empty: 不能为空。 - resend_email: - url_label: 确定要重新发送激活邮件吗? - url_text: 你也可以将上面的激活链接给该用户。 - login: - login_to_continue: 登录以继续 - info_sign: 没有账户?<1>注册 - info_login: 已经有账户?<1>登录 - agreements: 登录即表示您同意<1>隐私政策和<3>服务条款。 - forgot_pass: 忘记密码? - name: - label: 名字 - msg: - empty: 名字不能为空 - range: 名字应该在 4 到 30 个字符之间 - character: '只能由 "a-z"、"A-Z"、"0-9"、" - . _" 组成' - email: - label: 邮箱 - msg: - empty: 邮箱不能为空 - password: - label: 密码 - msg: - empty: 密码不能为空 - different: 两次输入密码不一致 - account_forgot: - page_title: 忘记密码 - btn_name: 发送恢复邮件 - send_success: >- - 如果存在邮箱为 {{mail}} 账户,你将很快收到一封重置密码的说明邮件。 - email: - label: 邮箱 - msg: - empty: 邮箱不能为空 - change_email: - btn_cancel: 取消 - btn_update: 更新电子邮件地址 - send_success: >- - 如果存在邮箱为 {{mail}} 的账户,你将很快收到一封重置密码的说明邮件。 - email: - label: 新的电子邮件地址 - msg: - empty: 邮箱不能为空。 - oauth: - connect: 连接到 {{ auth_name }} - remove: 移除 {{ auth_name }} - oauth_bind_email: - subtitle: 向你的账户添加恢复邮件地址。 - btn_update: 更新电子邮件地址 - email: - label: 邮箱 - msg: - empty: 邮箱不能为空。 - modal_title: 邮箱已经存在。 - modal_content: 该电子邮件地址已经注册。你确定要连接到已有账户吗? - modal_cancel: 更改邮箱 - modal_confirm: 连接到已有账户 - password_reset: - page_title: 密码重置 - btn_name: 重置我的密码 - reset_success: >- - 你已经成功更改密码;你将被重定向到登录页面。 - link_invalid: >- - 抱歉,此密码重置链接已失效。也许是你已经重置过密码了? - to_login: 前往登录页面 - password: - label: 密码 - msg: - empty: 密码不能为空。 - length: 密码长度在8-32个字符之间 - different: 两次输入密码不一致 - password_confirm: - label: 确认新密码 - settings: - page_title: 设置 - goto_modify: 前往修改 - nav: - profile: 我的资料 - notification: 通知 - account: 账号 - interface: 界面 - profile: - heading: 个人资料 - btn_name: 保存 - display_name: - label: 显示名称 - msg: 昵称不能为空。 - msg_range: 显示名称不能超过 30 个字符。 - username: - label: 用户名 - caption: 用户可以通过 "@用户名" 来提及你。 - msg: 用户名不能为空 - msg_range: 用户名不能超过 30 个字符。 - character: '只能由 "a-z"、"A-Z"、"0-9"、" - . _" 组成' - avatar: - label: 头像 - gravatar: Gravatar - gravatar_text: 你可以更改图像在 - custom: 自定义 - custom_text: 你可以上传你的图片。 - default: 系统 - msg: 请上传头像 - bio: - label: 关于我 - website: - label: 网站 - placeholder: "https://example.com" - msg: 网址格式不正确 - location: - label: 位置 - placeholder: "城市,国家" - notification: - heading: 邮件通知 - turn_on: 开启 - inbox: - label: 收件箱通知 - description: 你的提问有新的回答,评论,邀请回答和其他。 - all_new_question: - label: 所有新问题 - description: 获取所有新问题的通知。每周最多有50个问题。 - all_new_question_for_following_tags: - label: 所有关注标签的新问题 - description: 获取关注的标签下新问题通知。 - account: - heading: 账号 - change_email_btn: 更改邮箱 - change_pass_btn: 更改密码 - change_email_info: >- - 邮件已发送。请根据指引完成验证。 - email: - label: 电子邮件地址 - new_email: - label: 新的电子邮件地址 - msg: 新邮箱不能为空。 - pass: - label: 当前密码 - msg: 密码不能为空。 - password_title: 密码 - current_pass: - label: 当前密码 - msg: - empty: 当前密码不能为空 - length: 密码长度必须在 8 至 32 之间 - different: 两次输入的密码不匹配 - new_pass: - label: 新密码 - pass_confirm: - label: 确认新密码 - interface: - heading: 界面 - lang: - label: 界面语言 - text: 设置用户界面语言,在刷新页面后生效。 - my_logins: - title: 我的登录 - label: 使用这些账户登录或注册本网站。 - modal_title: 移除登录 - modal_content: 你确定要从账户里移除该登录? - modal_confirm_btn: 移除 - remove_success: 移除成功 - toast: - update: 更新成功 - update_password: 密码更新成功。 - flag_success: 感谢标记。 - forbidden_operate_self: 禁止对自己执行操作 - review: 您的修订将在审阅通过后显示。 - sent_success: 发送成功 - related_question: - title: 相关问题 - answers: 个回答 - invite_to_answer: - title: 受邀人 - desc: 邀请你认为可能知道答案的人。 - invite: 邀请回答 - add: 添加人员 - search: 搜索人员 - question_detail: - action: 操作 - Asked: 提问于 - asked: 提问于 - update: 修改于 - edit: 编辑于 - commented: 评论 - Views: 阅读次数 - Follow: 关注此问题 - Following: 已关注 - follow_tip: 关注此问题以接收通知 - answered: 回答于 - closed_in: 关闭于 - show_exist: 查看类似问题。 - useful: 有用的 - question_useful: 它是有用和明确的 - question_un_useful: 它不明确或没用的 - question_bookmark: 收藏该问题 - answer_useful: 这是有用的 - answer_un_useful: 它是没有用的 - answers: - title: 个回答 - score: 评分 - newest: 最新 - oldest: 最旧 - btn_accept: 采纳 - btn_accepted: 已被采纳 - write_answer: - title: 你的回答 - edit_answer: 编辑我的回答 - btn_name: 提交你的回答 - add_another_answer: 添加另一个回答 - confirm_title: 继续回答 - continue: 继续 - confirm_info: >- -

你确定要提交一个新的回答吗?

作为替代,你可以通过编辑来完善和改进之前的回答。

- empty: 回答内容不能为空。 - characters: 内容长度至少 6 个字符。 - tips: - header_1: 感谢你的回答 - li1_1: 请务必确定在 回答问题。提供详细信息并分享你的研究。 - li1_2: 用参考资料或个人经历来支持你所做的任何陈述。 - header_2: 但是 请避免... - li2_1: 请求帮助,寻求澄清,或答复其他答案。 - reopen: - confirm_btn: 重新打开 - title: 重新打开这个帖子 - content: 确定要重新打开吗? - list: - confirm_btn: 列表显示 - title: 列表中显示这个帖子 - content: 确定要列表中显示这个帖子吗? - unlist: - confirm_btn: 列表隐藏 - title: 从列表中隐藏这个帖子 - content: 确定要从列表中隐藏这个帖子吗? - pin: - title: 置顶该帖子 - content: 你确定要全局置顶吗?这个帖子将出现在所有帖子列表的顶部。 - confirm_btn: 置顶 - delete: - title: 删除 - question: >- - 我们不建议 删除有回答的帖子。因为这样做会使得后来的读者无法从该帖子中获得帮助。

如果删除过多有回答的帖子,你的账号将会被禁止提问。你确定要删除吗? - answer_accepted: >- -

我们不建议删除被采纳的回答。因为这样做会使得后来的读者无法从该帖子中获得帮助。

如果删除过多被采纳的回答,你的账号将会被禁止回答任何提问。你确定要删除吗? - other: 你确定要删除? - tip_answer_deleted: 该回答已被删除 - undelete_title: 撤销删除本帖 - undelete_desc: 你确定你要撤销删除吗? - btns: - confirm: 确认 - cancel: 取消 - edit: 编辑 - save: 保存 - delete: 删除 - undelete: 撤消删除 - list: 列表显示 - unlist: 列表隐藏 - unlisted: 已隐藏 - login: 登录 - signup: 注册 - logout: 退出 - verify: 验证 - add_question: 我要提问 - approve: 批准 - reject: 拒绝 - skip: 跳过 - discard_draft: 丢弃草稿 - pinned: 已置顶 - all: 全部 - question: 问题 - answer: 回答 - comment: 评论 - refresh: 刷新 - resend: 重新发送 - deactivate: 取消激活 - active: 激活 - suspend: 封禁 - unsuspend: 解禁 - close: 关闭 - reopen: 重新打开 - ok: 确定 - light: 浅色 - dark: 深色 - system_setting: 跟随系统 - default: 默认 - reset: 重置 - tag: 标签 - post_lowercase: 帖子 - filter: 筛选 - ignore: 忽略 - submit: 提交 - normal: 正常 - closed: 已关闭 - deleted: 已删除 - pending: 等待处理 - more: 更多 - search: - title: 搜索结果 - keywords: 关键词 - options: 选项 - follow: 关注 - following: 已关注 - counts: "{{count}} 个结果" - more: 更多 - sort_btns: - relevance: 相关性 - newest: 最新的 - active: 活跃的 - score: 评分 - more: 更多 - tips: - title: 高级搜索提示 - tag: "<1>[tag] 在指定标签中搜索" - user: "<1>user:username 根据作者搜索" - answer: "<1>answers:0 搜索未回答的问题" - score: "<1>score:3 评分 3+ 的帖子" - question: "<1>is:question 搜索问题" - is_answer: "<1>is:answer 搜索回答" - empty: 找不到任何相关的内容。
请尝试其他关键字,或者减少查找内容的长度。 - share: - name: 分享 - copy: 复制链接 - via: 分享到... - copied: 已复制 - facebook: 分享到 Facebook - twitter: 分享到 Twitter - cannot_vote_for_self: 你不能给自己的帖子投票。 - modal_confirm: - title: 发生错误... - account_result: - success: 你的账号已通过验证,即将返回首页。 - link: 返回首页 - invalid: >- - 抱歉,此验证链接已失效。也许你的账号已经激活了? - confirm_new_email: 你的电子邮箱已更新 - confirm_new_email_invalid: >- - 抱歉,此验证链接已失效。也许是你的邮箱已经成功更改了? - unsubscribe: - page_title: 退订 - success_title: 退订成功 - success_desc: 您已成功退订,并且将不会再收到我们的邮件。 - link: 更改设置 - question: - following_tags: 已关注的标签 - edit: 编辑 - save: 保存 - follow_tag_tip: 关注标签来筛选你的问题列表。 - hot_questions: 热门问题 - all_questions: 全部问题 - x_questions: "{{ count }} 个问题" - x_answers: "{{ count }} 个回答" - questions: 问题 - answers: 回答 - newest: 最新 - active: 活跃 - hot: 热门 - score: 评分 - unanswered: 未回答 - modified: 更新于 - answered: 回答于 - asked: 提问于 - closed: 已关闭 - follow_a_tag: 关注一个标签 - more: 更多 - personal: - overview: 概览 - answers: 回答 - answer: 回答 - questions: 问题 - question: 问题 - bookmarks: 收藏 - reputation: 声望 - comments: 评论 - votes: 得票 - newest: 最新 - score: 评分 - edit_profile: 编辑资料 - visited_x_days: "已访问 {{ count }} 天" - viewed: 浏览次数 - joined: 加入于 - last_login: 上次登录 - about_me: 关于我 - about_me_empty: "// Hello, World!" - top_answers: 高分回答 - top_questions: 高分问题 - stats: 状态 - list_empty: 没有找到相关的内容。
试试看其他选项卡? - accepted: 已采纳 - answered: 回答于 - asked: 提问于 - downvoted: 点踩 - mod_short: 版主 - mod_long: 版主 - x_reputation: 声望 - x_votes: 得票 - x_answers: 个回答 - x_questions: 个问题 - install: - title: 安装 - next: 下一步 - done: 完成 - config_yaml_error: 无法创建 config.yaml 文件。 - lang: - label: 请选择一种语言 - db_type: - label: 数据库引擎 - db_username: - label: 用户名 - placeholder: root - msg: 用户名不能为空 - db_password: - label: 密码 - placeholder: root - msg: 密码不能为空 - db_host: - label: 数据库主机 - placeholder: "db:3306" - msg: 数据库地址不能为空 - db_name: - label: 数据库名 - placeholder: 回答 - msg: 数据库名称不能为空。 - db_file: - label: 数据库文件 - placeholder: /data/answer.db - msg: 数据库文件不能为空。 - config_yaml: - title: 创建 config.yaml - label: 已创建 config.yaml 文件。 - desc: >- - 你可以手动在 <1>/var/wwww/xxx/ 目录中创建 <1>config.yaml 文件并粘贴以下文本。 - info: 完成后,点击“下一步”按钮。 - site_information: 站点信息 - admin_account: 管理员账号 - site_name: - label: 站点名称 - msg: 站点名称不能为空。 - msg_max_length: 站点名称长度不得超过 30 个字符。 - site_url: - label: 网站网址 - text: 此网站的网址。 - msg: - empty: 网址不能为空。 - incorrect: 网址格式不正确。 - max_length: 网址长度不得超过 512 个字符。 - contact_email: - label: 联系邮箱 - text: 负责本网站的主要联系人的电子邮件地址。 - msg: - empty: 联系人邮箱不能为空。 - incorrect: 联系人邮箱地址不正确。 - login_required: - label: 私有的 - switch: 需要登录 - text: 只有登录用户才能访问这个社区。 - admin_name: - label: 名字 - msg: 名字不能为空。 - character: '只能由 "a-z", "0-9", " - . _" 组成' - msg_max_length: 名字长度不能超过 30 个字符。 - admin_password: - label: 密码 - text: >- - 您需要此密码才能登录。请将其存储在一个安全的位置。 - msg: 密码不能为空。 - msg_min_length: 密码必须至少 8 个字符长。 - msg_max_length: 密码长度不能超过 32 个字符。 - admin_email: - label: 邮箱 - text: 您需要此电子邮件才能登录。 - msg: - empty: 邮箱不能为空。 - incorrect: 邮箱格式不正确。 - ready_title: 您的网站已准备好 - ready_desc: >- - 如果你想改变更多的设置,请访问 <1>管理区域;在网站菜单中找到它。 - good_luck: "玩得愉快,祝你好运!" - warn_title: 警告 - warn_desc: >- - 文件 <1>config.yaml 已存在。如果你要重置该文件中的任何配置项,请先删除它。 - install_now: 您可以尝试 <1>现在安装。 - installed: 已安裝 - installed_desc: >- - 你似乎已经安装过了。如果要重新安装,请先清除旧的数据库表。 - db_failed: 数据连接异常! - db_failed_desc: >- - 这或者意味着数据库信息在 <1>config.yaml 文件不正确,或者无法与数据库服务器建立联系。这可能意味着你的主机数据库服务器故障。 - counts: - views: 次浏览 - votes: 个点赞 - answers: 个回答 - accepted: 已被采纳 - page_error: - http_error: HTTP 错误 {{ code }} - desc_403: 您无权访问此页面。 - desc_404: 很抱歉,此页面不存在。 - desc_50X: 服务器遇到了一个错误,无法完成你的请求。 - back_home: 返回首页 - page_maintenance: - desc: "我们正在进行维护,我们将很快回来。" - nav_menus: - dashboard: 后台管理 - contents: 内容管理 - questions: 问题 - answers: 回答 - users: 用户管理 - flags: 举报管理 - settings: 站点设置 - general: 一般 - interface: 界面 - smtp: SMTP - branding: 品牌 - legal: 法律条款 - write: 撰写 - tos: 服务条款 - privacy: 隐私政策 - seo: SEO - customize: 自定义 - themes: 主题 - css_html: CSS/HTML - login: 登录 - privileges: 特权 - plugins: 插件 - installed_plugins: 已安装插件 - website_welcome: 欢迎来到 {{site_name}} - user_center: - login: 登录 - qrcode_login_tip: 请使用 {{ agentName }} 扫描二维码并登录。 - login_failed_email_tip: 登录失败,请允许此应用访问您的邮箱信息,然后重试。 - admin: - admin_header: - title: 后台管理 - dashboard: - title: 后台管理 - welcome: 欢迎来到管理后台! - site_statistics: 站点统计 - questions: "问题:" - answers: "回答:" - comments: "评论:" - votes: "投票:" - users: "用户:" - flags: "举报:" - reviews: "审查:" - site_health: 网站健康 - version: "版本" - https: "HTTPS:" - upload_folder: "上传文件夹:" - run_mode: "运行模式:" - private: 私有 - public: 公开 - smtp: "SMTP:" - timezone: "时区:" - system_info: 系统信息 - go_version: "Go版本:" - database: "数据库:" - database_size: "数据库大小:" - storage_used: "已用存储空间:" - uptime: "运行时间:" - links: 链接 - plugins: 插件 - github: GitHub - blog: 博客 - contact: 联系 - forum: 论坛 - documents: 文档 - feedback: 用户反馈 - support: 帮助 - review: 审查 - config: 配置 - update_to: 更新到 - latest: 最新版本 - check_failed: 校验失败 - "yes": "是" - "no": "否" - not_allowed: 拒绝 - allowed: 允许 - enabled: 已启用 - disabled: 停用 - writable: 可写 - not_writable: 不可写 - flags: - title: 举报 - pending: 等待处理 - completed: 已完成 - flagged: 被举报内容 - flagged_type: 标记了 {{ type }} - created: 创建于 - action: 操作 - review: 审查 - user_role_modal: - title: 更改用户状态为... - btn_cancel: 取消 - btn_submit: 提交 - new_password_modal: - title: 设置新密码 - form: - fields: - password: - label: 密码 - text: 用户将被退出,需要再次登录。 - msg: 密码的长度必须是8-32个字符。 - btn_cancel: 取消 - btn_submit: 提交 - edit_profile_modal: - title: 编辑资料 - form: - fields: - username: - label: 用户名 - msg_range: 用户名不能超过 30 个字符。 - email: - label: 电子邮件地址 - msg_invalid: 无效的邮箱地址 - edit_success: 修改成功 - btn_cancel: 取消 - btn_submit: 提交 - user_modal: - title: 添加新用户 - form: - fields: - users: - label: 批量添加用户 - placeholder: "John Smith, john@example.com, BUSYopr2\nAlice, alice@example.com, fpDntV8q" - text: 用逗号分隔“name, email, password”,每行一个用户。 - msg: "请输入用户的邮箱,每行一个。" - display_name: - label: 显示名称 - msg: 显示名称长度必须为 4-30 个字符 - email: - label: 邮箱 - msg: 邮箱无效。 - password: - label: 密码 - msg: 密码的长度必须是8-32个字符。 - btn_cancel: 取消 - btn_submit: 提交 - users: - title: 用户 - name: 名称 - email: 邮箱 - reputation: 声望 - created_at: 创建时间 - delete_at: 删除时间 - suspend_at: 封禁时间 - status: 状态 - role: 角色 - action: 操作 - change: 更改 - all: 全部 - staff: 工作人员 - more: 更多 - inactive: 不活跃 - suspended: 已封禁 - deleted: 已删除 - normal: 正常 - Moderator: 版主 - Admin: 管理员 - User: 用户 - filter: - placeholder: "按名称筛选,用户:id" - set_new_password: 设置新密码 - edit_profile: 编辑资料 - change_status: 更改状态 - change_role: 更改角色 - show_logs: 显示日志 - add_user: 添加用户 - deactivate_user: - title: 停用用户 - content: 未激活的用户必须重新验证他们的邮箱。 - delete_user: - title: 删除此用户 - content: 确定要删除此用户?此操作无法撤销! - remove: 移除内容 - label: 删除所有问题、 答案、 评论等 - text: 如果你只想删除用户账户,请不要选中此项。 - suspend_user: - title: 挂起此用户 - content: 被封禁的用户将无法登录。 - questions: - page_title: 问题 - unlisted: 已隐藏 - post: 标题 - votes: 得票数 - answers: 回答数 - created: 创建于 - status: 状态 - action: 操作 - change: 更改 - pending: 等待处理 - filter: - placeholder: "按标题过滤,问题:id" - answers: - page_title: 回答 - post: 标题 - votes: 得票数 - created: 创建于 - status: 状态 - action: 操作 - change: 更改 - filter: - placeholder: "按标题筛选,答案:id" - general: - page_title: 一般 - name: - label: 站点名称 - msg: 不能为空 - text: "站点的名称,作为站点的标题。" - site_url: - label: 网站网址 - msg: 网站网址不能为空。 - validate: 请输入一个有效的 URL。 - text: 此网站的地址。 - short_desc: - label: 简短站点描述 - msg: 简短网站描述不能为空。 - text: "简短的标语,作为网站主页的标题(Html 的 title 标签)。" - desc: - label: 站点描述 - msg: 网站描述不能为空。 - text: "使用一句话描述本站,作为网站的描述(Html 的 meta 标签)。" - contact_email: - label: 联系邮箱 - msg: 联系人邮箱不能为空。 - validate: 联系人邮箱无效。 - text: 本网站的主要联系邮箱地址。 - check_update: - label: 软件更新 - text: 自动检查软件更新 - interface: - page_title: 界面 - language: - label: 界面语言 - msg: 不能为空 - text: 设置用户界面语言,在刷新页面后生效。 - time_zone: - label: 时区 - msg: 时区不能为空。 - text: 选择一个与您相同时区的城市。 - smtp: - page_title: SMTP - from_email: - label: 发件人邮箱 - msg: 发件人邮箱不能为空。 - text: 用于发送邮件的地址。 - from_name: - label: 发件人 - msg: 不能为空 - text: 发件人的名字。 - smtp_host: - label: SMTP 主机 - msg: 不能为空 - text: 邮件服务器 - encryption: - label: 加密 - msg: 不能为空 - text: 对于大多数服务器而言,SSL 是推荐开启的。 - ssl: SSL - tls: TLS - none: 无加密 - smtp_port: - label: SMTP 端口 - msg: SMTP 端口必须在 1 ~ 65535 之间。 - text: 邮件服务器的端口号。 - smtp_username: - label: SMTP 用户名 - msg: 不能为空 - smtp_password: - label: SMTP 密码 - msg: 不能为空 - test_email_recipient: - label: 测试收件邮箱 - text: 提供用于接收测试邮件的邮箱地址。 - msg: 测试收件邮箱无效 - smtp_authentication: - label: 启用身份验证 - title: SMTP 身份验证 - msg: 不能为空 - "yes": "是" - "no": "否" - branding: - page_title: 品牌 - logo: - label: 网站标志(Logo) - msg: 图标不能为空。 - text: 在你的网站左上方的Logo图标。使用一个高度为56,长宽比大于3:1的宽长方形图像。如果留空,将显示网站标题文本。 - mobile_logo: - label: 移动端 Logo - text: 在你的网站的移动版上使用的标志。使用一个高度为56的宽矩形图像。如果留空,将使用 "Logo"设置中的图像。 - square_icon: - label: 方形图标 - msg: 方形图标不能为空。 - text: 用作元数据图标的基础的图像。最好是大于512x512。 - favicon: - label: 收藏夹图标 - text: 网站的图标。要在 CDN 正常工作,它必须是 png。 将调整大小到32x32。如果留空,将使用“方形图标”。 - legal: - page_title: 法律条款 - terms_of_service: - label: 服务条款 - text: "您可以在此添加服务内容的条款。如果您已经在别处托管了文档,请在这里提供完整的URL。" - privacy_policy: - label: 隐私政策 - text: "您可以在此添加隐私政策内容。如果您已经在别处托管了文档,请在这里提供完整的URL。" - write: - page_title: 编辑 - restrict_answer: - title: 限制一个回答 - label: 每个用户对于每个问题只能有一个回答 - text: "用户可以使用编辑按钮优化已有的回答" - recommend_tags: - label: 推荐标签 - text: "请在上方输入标签固定链接,每行一个标签。" - required_tag: - title: 必需的标签 - label: 根据需要设置推荐标签 - text: "每个新问题必须至少有一个推荐标签。" - reserved_tags: - label: 保留标签 - text: "保留的标签只能由版主添加到一个帖子中。" - seo: - page_title: 搜索引擎优化 - permalink: - label: 固定链接 - text: 自定义URL结构可以提高可用性,以及你的链接的向前兼容性。 - robots: - label: robots.txt - text: 这将永久覆盖任何相关的网站设置。 - themes: - page_title: 主题 - themes: - label: 主题 - text: 选择一个现有主题。 - color_scheme: - label: 配色方案 - navbar_style: - label: 导航栏样式 - primary_color: - label: 主色调 - text: 修改您主题使用的颜色 - css_and_html: - page_title: CSS 与 HTML - custom_css: - label: 自定义 CSS - text: > - - head: - label: 头部 - text: > - - header: - label: 页眉 - text: > - - footer: - label: 页脚 - text: 这将在 之前插入. - sidebar: - label: 侧边栏 - text: 这将插入侧边栏中。 - login: - page_title: 登录 - membership: - title: 会员 - label: 允许新注册 - text: 关闭以防止任何人创建新账户。 - email_registration: - title: 邮箱注册 - label: 允许邮箱注册 - text: 关闭以阻止任何人通过邮箱创建新账户。 - allowed_email_domains: - title: 允许的邮箱域 - text: 允许注册账户的邮箱域。每行一个域名。留空时忽略。 - private: - title: 非公开的 - label: 需要登录 - text: 只有登录用户才能访问这个社区。 - password_login: - title: 密码登录 - label: 允许使用邮箱和密码登录 - text: "警告:如果您未配置过其他登录方式,关闭密码登录后您则可能无法登录。" - installed_plugins: - title: 已安装插件 - plugin_link: 插件扩展功能。您可以在<1>插件仓库中找到插件。 - filter: - all: 全部 - active: 已启用 - inactive: 未启用 - outdated: 已过期 - plugins: - label: 插件 - text: 选择一个现有的插件。 - name: 名称 - version: 版本 - status: 状态 - action: 操作 - deactivate: 停用 - activate: 启用 - settings: 设置 - settings_users: - title: 用户 - avatar: - label: 默认头像 - text: 没有自定义头像的用户。 - gravatar_base_url: - label: Gravatar 根路径 URL - text: Gravatar 提供商的 API 基础的 URL。当为空时忽略。 - profile_editable: - title: 个人资料可编辑 - allow_update_display_name: - label: 允许用户修改显示名称 - allow_update_username: - label: 允许用户修改用户名 - allow_update_avatar: - label: 允许用户修改个人头像 - allow_update_bio: - label: 允许用户修改个人介绍 - allow_update_website: - label: 允许用户修改个人主页网址 - allow_update_location: - label: 允许用户更改位置 - privilege: - title: 特权 - level: - label: 级别所需声望 - text: 选择特权所需的声望值 - msg: - should_be_number: 输入必须是数字 - number_larger_1: 数字应该大于等于 1 - form: - optional: (选填) - empty: 不能为空 - invalid: 是无效的 - btn_submit: 保存 - not_found_props: "所需属性 {{ key }} 未找到。" - select: 选择 - page_review: - review: 评论 - proposed: 提案 - question_edit: 问题编辑 - answer_edit: 回答编辑 - tag_edit: '标签管理: 编辑标签' - edit_summary: 编辑备注 - edit_question: 编辑问题 - edit_answer: 编辑回答 - edit_tag: 编辑标签 - empty: 没有剩余的审核任务。 - approve_revision_tip: 您是否批准此修订? - approve_flag_tip: 您是否批准此举报? - approve_post_tip: 您是否批准此帖子? - approve_user_tip: 您是否批准此修订? - suggest_edits: 建议的编辑 - flag_post: 举报帖子 - flag_user: 举报用户 - queued_post: 排队的帖子 - queued_user: 排队用户 - filter_label: 类型 - reputation: 声望值 - flag_post_type: 举报这个帖子的类型是 {{ type }} - flag_user_type: 举报这个用户的类型是 {{ type }} - edit_post: 编辑帖子 - list_post: 文章列表 - unlist_post: 隐藏的帖子 - timeline: - undeleted: 取消删除 - deleted: 删除 - downvote: 反对 - upvote: 点赞 - accept: 采纳 - cancelled: 已取消 - commented: '评论:' - rollback: 回滚 - edited: 最后编辑于 - answered: 回答于 - asked: 提问于 - closed: 关闭 - reopened: 重新开启 - created: 创建于 - pin: 已置顶 - unpin: 取消置頂 - show: 已显示 - hide: 已隐藏 - title: "历史记录" - tag_title: "时间线" - show_votes: "显示投票" - n_or_a: N/A - title_for_question: "时间线" - title_for_answer: "{{ title }} 的 {{ author }} 回答时间线" - title_for_tag: "时间线" - datetime: 日期时间 - type: 类型 - by: 由 - comment: 评论 - no_data: "空空如也" - users: - title: 用户 - users_with_the_most_reputation: 本周声望最高的用户 - users_with_the_most_vote: 本周投票最多的用户 - staffs: 我们的社区工作人员 - reputation: 声望值 - votes: 投票 - prompt: - leave_page: 确定要离开此页面? - changes_not_save: 您的更改尚未保存 - draft: - discard_confirm: 您确定要丢弃您的草稿吗? - messages: - post_deleted: 该帖子已被删除。 - post_pin: 该帖子已被置顶。 - post_unpin: 该帖子已被取消置顶。 - post_hide_list: 此帖子已经从列表中隐藏。 - post_show_list: 该帖子已显示到列表中。 - post_reopen: 这个帖子已被重新打开. - post_list: 这个帖子已经被显示 - post_unlist: 这个帖子已经被隐藏 - post_pending: 您的帖子正在等待审核。它将在它获得批准后可见。 From e9c925082b7a11391e59082f308989c37c2fe0d6 Mon Sep 17 00:00:00 2001 From: kumfo Date: Mon, 19 Aug 2024 15:20:38 +0800 Subject: [PATCH 16/64] Revert "fix(dev): delete test file" This reverts commit c50e63dd34ce9ddb7babd769146278ef41e5bfcb. --- dev/i18n/zh_CN.yaml | 2035 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 2035 insertions(+) create mode 100644 dev/i18n/zh_CN.yaml diff --git a/dev/i18n/zh_CN.yaml b/dev/i18n/zh_CN.yaml new file mode 100644 index 000000000..6613bfc22 --- /dev/null +++ b/dev/i18n/zh_CN.yaml @@ -0,0 +1,2035 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +# The following fields are used for back-end +backend: + base: + success: + other: 成功。 + unknown: + other: 未知错误。 + request_format_error: + other: 请求格式错误。 + unauthorized_error: + other: 未授权。 + database_error: + other: 数据服务器错误。 + forbidden_error: + other: 禁止访问。 + duplicate_request_error: + other: 重复提交。 + action: + report: + other: 举报 + edit: + other: 编辑 + delete: + other: 删除 + close: + other: 关闭 + reopen: + other: 重新打开 + forbidden_error: + other: 禁止访问。 + pin: + other: 置顶 + hide: + other: 列表隐藏 + unpin: + other: 取消置顶 + show: + other: 列表显示 + invite_someone_to_answer: + other: 编辑 + undelete: + other: 撤消删除 + role: + name: + user: + other: 用户 + admin: + other: 管理员 + moderator: + other: 版主 + description: + user: + other: 默认没有特殊权限。 + admin: + other: 拥有管理网站的全部权限。 + moderator: + other: 拥有除访问后台管理以外的所有权限。 + privilege: + level_1: + description: + other: 级别 1(少量声望要求,适合私有团队、群组) + level_2: + description: + other: 级别 2(低声望要求,适合初启动的社区) + level_3: + description: + other: 级别 3(高声望要求,适合成熟的社区) + level_custom: + description: + other: 自定义等级 + rank_question_add_label: + other: 提问 + rank_answer_add_label: + other: 写答案 + rank_comment_add_label: + other: 写评论 + rank_report_add_label: + other: 举报 + rank_comment_vote_up_label: + other: 点赞评论 + rank_link_url_limit_label: + other: 每次发布超过 2 个链接 + rank_question_vote_up_label: + other: 点赞问题 + rank_answer_vote_up_label: + other: 点赞答案 + rank_question_vote_down_label: + other: 点踩问题 + rank_answer_vote_down_label: + other: 点踩答案 + rank_invite_someone_to_answer_label: + other: 邀请回答 + rank_tag_add_label: + other: 创建新标签 + rank_tag_edit_label: + other: 编辑标签描述(需要审核) + rank_question_edit_label: + other: 编辑别人的问题(需要审核) + rank_answer_edit_label: + other: 编辑别人的答案(需要审核) + rank_question_edit_without_review_label: + other: 编辑别人的问题无需审核 + rank_answer_edit_without_review_label: + other: 编辑别人的答案无需审核 + rank_question_audit_label: + other: 审核问题编辑 + rank_answer_audit_label: + other: 审核回答编辑 + rank_tag_audit_label: + other: 审核标签编辑 + rank_tag_edit_without_review_label: + other: 编辑标签描述无需审核 + rank_tag_synonym_label: + other: 管理标签同义词 + email: + other: 邮箱 + e_mail: + other: 邮箱 + password: + other: 密码 + pass: + other: 密码 + original_text: + other: 本帖 + email_or_password_wrong_error: + other: 邮箱和密码不匹配。 + error: + common: + invalid_url: + other: 无效的 URL。 + password: + space_invalid: + other: 密码不得含有空格。 + admin: + cannot_update_their_password: + other: 你无法修改自己的密码。 + cannot_edit_their_profile: + other: 您不能修改您的个人资料。 + cannot_modify_self_status: + other: 你无法修改自己的状态。 + email_or_password_wrong: + other: 邮箱和密码不匹配。 + answer: + not_found: + other: 没有找到答案。 + cannot_deleted: + other: 没有删除权限。 + cannot_update: + other: 没有更新权限。 + question_closed_cannot_add: + other: 问题已关闭,无法添加。 + comment: + edit_without_permission: + other: 不允许编辑评论。 + not_found: + other: 评论未找到。 + cannot_edit_after_deadline: + other: 评论时间太久,无法修改。 + email: + duplicate: + other: 邮箱已存在。 + need_to_be_verified: + other: 邮箱需要验证。 + verify_url_expired: + other: 邮箱验证的网址已过期,请重新发送邮件。 + illegal_email_domain_error: + other: 此邮箱不在允许注册的邮箱域中。请使用其他邮箱尝试。 + lang: + not_found: + other: 语言文件未找到。 + object: + captcha_verification_failed: + other: 验证码错误。 + disallow_follow: + other: 你不能关注。 + disallow_vote: + other: 你不能投票。 + disallow_vote_your_self: + other: 你不能为自己的帖子投票。 + not_found: + other: 对象未找到。 + verification_failed: + other: 验证失败。 + email_or_password_incorrect: + other: 邮箱和密码不匹配。 + old_password_verification_failed: + other: 旧密码验证失败。 + new_password_same_as_previous_setting: + other: 新密码和旧密码相同。 + already_deleted: + other: 该帖子已被删除。 + meta: + object_not_found: + other: Meta 对象未找到 + question: + already_deleted: + other: 该帖子已被删除。 + under_review: + other: 您的帖子正在等待审核。它将在它获得批准后可见。 + not_found: + other: 问题未找到。 + cannot_deleted: + other: 没有删除权限。 + cannot_close: + other: 没有关闭权限。 + cannot_update: + other: 没有更新权限。 + rank: + fail_to_meet_the_condition: + other: 声望值未达到要求。 + vote_fail_to_meet_the_condition: + other: 感谢投票。你至少需要 {{.Rank}} 声望才能投票。 + no_enough_rank_to_operate: + other: 你至少需要 {{.Rank}} 声望才能执行此操作。 + report: + handle_failed: + other: 报告处理失败。 + not_found: + other: 报告未找到。 + tag: + already_exist: + other: 标签已存在。 + not_found: + other: 标签未找到。 + recommend_tag_not_found: + other: 推荐标签不存在。 + recommend_tag_enter: + other: 请选择至少一个必选标签。 + not_contain_synonym_tags: + other: 不应包含同义词标签。 + cannot_update: + other: 没有更新权限。 + is_used_cannot_delete: + other: 你不能删除这个正在使用的标签。 + cannot_set_synonym_as_itself: + other: 你不能将当前标签设为自己的同义词。 + smtp: + config_from_name_cannot_be_email: + other: 发件人名称不能是邮箱地址。 + theme: + not_found: + other: 主题未找到。 + revision: + review_underway: + other: 目前无法编辑,有一个版本在审阅队列中。 + no_permission: + other: 无权限修改。 + user: + external_login_missing_user_id: + other: 第三方平台没有提供唯一的 UserID,所以你不能登录,请联系网站管理员。 + external_login_unbinding_forbidden: + other: 请在移除此登录之前为你的账户设置登录密码。 + email_or_password_wrong: + other: + other: 邮箱和密码不匹配。 + not_found: + other: 用户未找到。 + suspended: + other: 用户已被封禁。 + username_invalid: + other: 用户名无效。 + username_duplicate: + other: 用户名已被使用。 + set_avatar: + other: 头像设置错误。 + cannot_update_your_role: + other: 你不能修改自己的角色。 + not_allowed_registration: + other: 该网站暂未开放注册。 + not_allowed_login_via_password: + other: 该网站暂不支持密码登录。 + access_denied: + other: 拒绝访问 + page_access_denied: + other: 您没有权限访问此页面。 + add_bulk_users_format_error: + other: "发生错误,{{.Field}} 格式错误,在 '{{.Content}}' 行数 {{.Line}}. {{.ExtraMessage}}" + add_bulk_users_amount_error: + other: "一次性添加的用户数量应在 1-{{.MaxAmount}} 之间。" + config: + read_config_failed: + other: 读取配置失败 + database: + connection_failed: + other: 数据库连接失败 + create_table_failed: + other: 创建表失败 + install: + create_config_failed: + other: 无法创建 config.yaml 文件。 + upload: + unsupported_file_format: + other: 不支持的文件格式。 + site_info: + config_not_found: + other: 未找到网站的该配置信息。 + reason: + spam: + name: + other: 垃圾信息 + desc: + other: 这个帖子是一个广告,或是破坏性行为。它对当前的主题无帮助或无关。 + rude_or_abusive: + name: + other: 粗鲁或辱骂的 + desc: + other: + - 一个有理智的人都会认为这种内容不适合进行尊重性的讨论。 + - 论坛 + a_duplicate: + name: + other: 重复内容 + desc: + other: 该问题有人问过,而且已经有了答案。 + placeholder: + other: 输入已有的问题链接 + not_a_answer: + name: + other: 不是答案 + desc: + other: + - 这张贴作为答案,但它不会试图回答 + - 这可能是一个编辑、一个评论、另一个问题。 + - 或全部删除。 + no_longer_needed: + name: + other: 不再需要 + desc: + other: 该评论已过时,对话性质或与此帖子无关。 + something: + name: + other: 其他原因 + desc: + other: 此帖子需要工作人员注意,因为是上述所列以外的其他理由。 + placeholder: + other: 让我们具体知道你关心的什么 + community_specific: + name: + other: 社区特定原因 + desc: + other: 该问题不符合社区准则。 + not_clarity: + name: + other: 需要细节或澄清 + desc: + other: 该问题目前涵盖多个问题。它应该侧重在一个问题上。 + looks_ok: + name: + other: 看起来没问题 + desc: + other: 这个帖子是好的,不是低质量。 + needs_edit: + name: + other: 需要编辑,我已做了修改。 + desc: + other: 改进和纠正你自己帖子中的问题。 + needs_close: + name: + other: 需要关闭 + desc: + other: 关闭的问题不能回答,但仍然可以编辑、投票和评论。 + needs_delete: + name: + other: 需要删除 + desc: + other: 该帖子将被删除。 + question: + close: + duplicate: + name: + other: 垃圾信息 + desc: + other: 此问题以前就有人问过,而且已经有了答案。 + guideline: + name: + other: 社区特定原因 + desc: + other: 该问题不符合社区准则。 + multiple: + name: + other: 需要细节或澄清 + desc: + other: + - 该问题目前涵盖多个问题。它应该侧重在一个问题上。 + - 只关注一个问题。 + other: + name: + other: 其他原因 + desc: + other: 该帖子存在上面没有列出的另一个原因。 + operation_type: + asked: + other: 提问于 + answered: + other: 回答于 + modified: + other: 修改于 + deleted_title: + other: 删除的问题 + notification: + action: + update_question: + other: 更新了问题 + answer_the_question: + other: 回答了问题 + update_answer: + other: 更新了答案 + accept_answer: + other: 采纳了答案 + comment_question: + other: 评论了问题 + comment_answer: + other: 评论了答案 + reply_to_you: + other: 回复了你 + mention_you: + other: 提到了你 + your_question_is_closed: + other: 你的问题已被关闭 + your_question_was_deleted: + other: 你的问题已被删除 + your_answer_was_deleted: + other: 你的答案已被删除 + your_comment_was_deleted: + other: 你的评论已被删除 + up_voted_question: + other: 点赞问题 + down_voted_question: + other: 点踩问题 + up_voted_answer: + other: 点赞答案 + down_voted_answer: + other: 点踩回答 + up_voted_comment: + other: 点赞评论 + invited_you_to_answer: + other: 邀请你回答 + email_tpl: + change_email: + title: + other: "[{{.SiteName}}] 确认你的新邮箱地址" + body: + other: "请点击以下链接确认你在 {{.SiteName}} 上的新邮箱地址:
\n{{.ChangeEmailUrl}}

\n\n如果你没有请求此更改,请忽略此邮件。\n" + new_answer: + title: + other: "[{{.SiteName}}] {{.DisplayName}} 回答了你的问题" + body: + other: "{{.QuestionTitle}}

\n\n{{.DisplayName}}:
\n
{{.AnswerSummary}}

\n在 {{.SiteName}} 上查看

\n\n--
\n取消订阅" + invited_you_to_answer: + title: + other: "[{{.SiteName}}] {{.DisplayName}} 邀请您回答问题" + body: + other: "{{.QuestionTitle}}

\n\n{{.DisplayName}}:
\n
我想你可能知道答案。

\n在 {{.SiteName}} 上查看

\n\n--
\n取消订阅" + new_comment: + title: + other: "[{{.SiteName}}] {{.DisplayName}} 评论了你的帖子" + body: + other: "{{.QuestionTitle}}

\n\n{{.DisplayName}}:
\n
{{.CommentSummary}}

\n在 {{.SiteName}} 上查看

\n\n--
\n取消订阅" + new_question: + title: + other: "[{{.SiteName}}] 新问题: {{.QuestionTitle}}" + body: + other: "{{.QuestionTitle}}
\n{{.Tags}}

\n\n--
\n取消订阅" + pass_reset: + title: + other: "[{{.SiteName }}] 重置密码" + body: + other: "有人要求在 [{{.SiteName}}] 上重置你的密码。

\n\n如果这不是你的操作,请安心忽略此电子邮件。

\n\n请点击以下链接设置一个新密码:
\n{{.PassResetUrl}}\n" + register: + title: + other: "[{{.SiteName}}] 确认你的新账户" + body: + other: "欢迎加入 {{.SiteName}}!

\n\n请点击以下链接确认并激活你的新账户:
\n{{.RegisterUrl}}

\n\n如果上面的链接不能点击,请将其复制并粘贴到你的浏览器地址栏中。\n" + test: + title: + other: "[{{.SiteName}}] 测试邮件" + body: + other: "这是一封测试邮件。" + action_activity_type: + upvote: + other: 点赞 + upvoted: + other: 点赞 + downvote: + other: 点踩 + downvoted: + other: 点踩 + accept: + other: 采纳 + accepted: + other: 已采纳 + edit: + other: 编辑 + review: + queued_post: + other: 排队的帖子 + flagged_post: + other: 举报的帖子 + suggested_post_edit: + other: 建议的编辑 + reaction: + tooltip: + other: "{{ .Names }} 以及另外 {{ .Count }} 个..." +plugin: + s3_cdn: + backend: + info: + name: + other: S3 存储 CDN + description: + other: 上传文件到S3存储 + config: + endpoint: + title: + other: Endpoint + description: + other: S3存储的Endpoint + bucket_name: + title: + other: Bucket名称 + description: + other: S3存储的Bucket名称 + object_key_prefix: + title: + other: 对象Key前缀 + description: + other: 对象键的前缀,如'answer/data/',以'/'结尾 + access_key_id: + title: + other: AccessKeyID + description: + other: S3存储的AccessKeyID + access_key_secret: + title: + other: AccessKeySecret + description: + other: S3存储的AccessKeySecret + access_token: + title: + other: AccessToken + description: + other: 访问 S3 所需的 AccessToken + visit_url_prefix: + title: + other: 访问URL前缀 + description: + other: 上传静态文件CDN最终访问地址的前缀,以 '/' 结尾 https://static.example.com/xxx/ + max_file_size: + title: + other: 文件最大大小(MB) + description: + other: 限制上传文件的最大大小,单位MB,默认为10MB + region: + title: + other: 区域(Region) + description: + other: S3存储区域 + disable_ssl: + title: + other: 禁用SSL + description: + other: 我们建议您使用SSL访问S3存储。如果您想禁用SSL,请选中此选项。 + err: + mis_storage_config: + other: 错误的存储配置导致上传失败 + file_not_found: + other: 文件未找到 + unsupported_file_type: + other: 不支持的文件类型 + over_file_size_limit: + other: 超过文件大小限制 + upload_file_failed: + other: 上传文件失败 + aliyunoss_cdn: + backend: + info: + name: + other: 阿里云CDN OSS存储 + description: + other: 上传文件到阿里云CDN OSS存储 + config: + endpoint: + title: + other: Endpoint + description: + other: 阿里云OSS存储的Endpoint + bucket_name: + title: + other: Bucket名称 + description: + other: 阿里云OSS存储的Bucket名称 + object_key_prefix: + title: + other: 对象Key前缀 + description: + other: 对象键的前缀,如'answer/data/',以'/'结尾 + access_key_id: + title: + other: AccessKeyID + description: + other: 阿里云OSS存储的AccessKeyID + access_key_secret: + title: + other: AccessKeySecret + description: + other: 阿里云OSS存储的AccessKeySecret + visit_url_prefix: + title: + other: 访问URL前缀 + description: + other: CDN最终访问地址的前缀,以 '/' 结尾 https://example.com/xxx/ + max_file_size: + title: + other: 最大文件大小(MB) + description: + other: 限制上传文件的最大大小,单位为MB,默认为 10MB + err: + mis_storage_config: + other: 错误的存储配置导致上传失败 + file_not_found: + other: 文件未找到 + unsupported_file_type: + other: 不支持的文件类型 + over_file_size_limit: + other: 超过文件大小限制 + upload_file_failed: + other: 上传文件失败 +# The following fields are used for interface presentation(Front-end) +ui: + how_to_format: + title: 如何排版 + desc: >- +
  • 添加链接

    <https://url.com>

    [标题](https://url.com)
  • 段落之间使用空行分隔

  • _斜体_ 或者 **粗体**

  • 使用 4 个空格缩进代码

  • 在行首添加 > 表示引用

  • 反引号进行转义 `像 _这样_`

  • 使用 ``` 创建代码块

    ```
    这是代码块
    ```
+ pagination: + prev: 上一页 + next: 下一页 + page_title: + question: 问题 + questions: 问题 + tag: 标签 + tags: 标签 + tag_wiki: 标签维基 + create_tag: 创建标签 + edit_tag: 编辑标签 + ask_a_question: 提问题 + edit_question: 编辑问题 + edit_answer: 编辑回答 + search: 搜索 + posts_containing: 帖子包含 + settings: 设置 + notifications: 通知 + login: 登录 + sign_up: 注册 + account_recovery: 账号恢复 + account_activation: 账号激活 + confirm_email: 确认电子邮件 + account_suspended: 账号已被封禁 + admin: 后台管理 + change_email: 修改邮箱 + install: Answer 安装 + upgrade: Answer 升级 + maintenance: 网站维护 + users: 用户 + oauth_callback: 处理中 + http_404: HTTP 错误 404 + http_50X: HTTP 错误 500 + http_403: HTTP 错误 403 + logout: 退出 + notifications: + title: 通知 + inbox: 收件箱 + achievement: 成就 + new_alerts: 新通知 + all_read: 全部标记为已读 + show_more: 显示更多 + someone: 有人 + inbox_type: + all: 全部 + posts: 帖子 + invites: 邀请 + votes: 投票 + suspended: + title: 你的账号账号已被封禁 + until_time: "你的账号被封禁直到 {{ time }}。" + forever: 你的账号已被永久封禁。 + end: 你违反了我们的社区准则。 + contact_us: 联系我们 + editor: + blockquote: + text: 引用 + bold: + text: 粗体 + chart: + text: 图表 + flow_chart: 流程图 + sequence_diagram: 时序图 + class_diagram: 类图 + state_diagram: 状态图 + entity_relationship_diagram: 实体关系图 + user_defined_diagram: 用户自定义图表 + gantt_chart: 甘特图 + pie_chart: 饼图 + code: + text: 代码块 + add_code: 添加代码块 + form: + fields: + code: + label: 代码块 + msg: + empty: 代码块不能为空 + language: + label: 语言 + placeholder: 自动识别 + btn_cancel: 取消 + btn_confirm: 添加 + formula: + text: 公式 + options: + inline: 行内公式 + block: 块级公式 + heading: + text: 标题 + options: + h1: 标题 1 + h2: 标题 2 + h3: 标题 3 + h4: 标题 4 + h5: 标题 5 + h6: 标题 6 + help: + text: 帮助 + hr: + text: 水平线 + image: + text: 图片 + add_image: 添加图片 + tab_image: 上传图片 + form_image: + fields: + file: + label: 图像文件 + btn: 选择图片 + msg: + empty: 请选择图片文件。 + only_image: 只能上传图片文件。 + max_size: 文件大小不能超过 4 MB。 + desc: + label: 描述 + tab_url: 图片地址 + form_url: + fields: + url: + label: 图片地址 + msg: + empty: 图片地址不能为空 + name: + label: 描述 + btn_cancel: 取消 + btn_confirm: 添加 + uploading: 上传中 + indent: + text: 缩进 + outdent: + text: 减少缩进 + italic: + text: 斜体 + link: + text: 超链接 + add_link: 添加超链接 + form: + fields: + url: + label: 链接 + msg: + empty: 链接不能为空。 + name: + label: 描述 + btn_cancel: 取消 + btn_confirm: 添加 + ordered_list: + text: 有序列表 + unordered_list: + text: 无序列表 + table: + text: 表格 + heading: 表头 + cell: 单元格 + close_modal: + title: 关闭原因是... + btn_cancel: 取消 + btn_submit: 提交 + remark: + empty: 不能为空。 + msg: + empty: 请选择一个原因。 + report_modal: + flag_title: 我举报这篇帖子的原因是... + close_title: 我关闭这篇帖子的原因是... + review_question_title: 审查问题 + review_answer_title: 审查回答 + review_comment_title: 审查评论 + btn_cancel: 取消 + btn_submit: 提交 + remark: + empty: 不能为空 + msg: + empty: 请选择一个原因。 + not_a_url: URL 格式不正确。 + url_not_match: URL 来源与当前网站不匹配。 + tag_modal: + title: 创建新标签 + form: + fields: + display_name: + label: 显示名称 + msg: + empty: 显示名称不能为空。 + range: 显示名称不能超过 35 个字符。 + slug_name: + label: URL 固定链接 + desc: URL 固定链接不能超过 35 个字符。 + msg: + empty: URL 固定链接不能为空。 + range: URL 固定链接不能超过 35 个字符。 + character: URL 固定链接包含非法字符。 + desc: + label: 描述 + revision: + label: 编辑历史 + edit_summary: + label: 编辑备注 + placeholder: >- + 简单描述更改原因(更正拼写、修复语法、改进格式) + btn_cancel: 取消 + btn_submit: 提交 + btn_post: 发布新标签 + tag_info: + created_at: 创建于 + edited_at: 编辑于 + history: 历史 + synonyms: + title: 同义词 + text: 以下标签将被重置到 + empty: 此标签目前没有同义词。 + btn_add: 添加同义词 + btn_edit: 编辑 + btn_save: 保存 + synonyms_text: 以下标签将被重置到 + delete: + title: 删除标签 + tip_with_posts: >- +

我们不允许 删除带有帖子的标签

请先从帖子中移除此标签。

+ tip_with_synonyms: >- +

我们不允许 删除带有同义词的标签

请先从此标签中删除同义词。

+ tip: 确定要删除吗? + close: 关闭 + edit_tag: + title: 编辑标签 + default_reason: 编辑标签 + default_first_reason: 添加标签 + btn_save_edits: 保存更改 + btn_cancel: 取消 + dates: + long_date: MM 月 DD 日 + long_date_with_year: "YYYY 年 MM 月 DD 日" + long_date_with_time: "YYYY 年 MM 月 DD 日 HH:mm" + now: 刚刚 + x_seconds_ago: "{{count}} 秒前" + x_minutes_ago: "{{count}} 分钟前" + x_hours_ago: "{{count}} 小时前" + hour: 小时 + day: 天 + hours: 小时 + days: 日 + reaction: + heart: 爱心 + smile: 微笑 + frown: 愁 + btn_label: 添加或删除回应。 + undo_emoji: 撤销 {{ emoji }} 回应 + react_emoji: 用 {{ emoji }} 回应 + unreact_emoji: 撤销 {{ emoji }} + comment: + btn_add_comment: 添加评论 + reply_to: 回复 + btn_reply: 回复 + btn_edit: 编辑 + btn_delete: 删除 + btn_flag: 举报 + btn_save_edits: 保存更改 + btn_cancel: 取消 + show_more: "{{count}} 条剩余评论" + tip_question: >- + 使用评论提问更多信息或者提出改进意见。避免在评论里回答问题。 + tip_answer: >- + 使用评论对回答者进行回复,或者通知回答者你已更新了问题的内容。如果要补充或者完善问题的内容,请在原问题中更改。 + tip_vote: 它给帖子添加了一些有用的内容 + edit_answer: + title: 编辑回答 + default_reason: 编辑回答 + default_first_reason: 添加答案 + form: + fields: + revision: + label: 编辑历史 + answer: + label: 回答内容 + feedback: + characters: 内容长度至少 6 个字符 + edit_summary: + label: 编辑摘要 + placeholder: >- + 简单描述更改原因(更正拼写、修复语法、改进格式) + btn_save_edits: 保存更改 + btn_cancel: 取消 + tags: + title: 标签 + sort_buttons: + popular: 热门 + name: 名称 + newest: 最新 + button_follow: 关注 + button_following: 已关注 + tag_label: 个问题 + search_placeholder: 通过标签名称过滤 + no_desc: 此标签无描述。 + more: 更多 + ask: + title: 新增问题 + edit_title: 编辑问题 + default_reason: 编辑问题 + default_first_reason: 新增问题 + similar_questions: 相似问题 + form: + fields: + revision: + label: 修订版本 + title: + label: 标题 + placeholder: 请详细描述你的问题,想象你在问一个人 + msg: + empty: 标题不能为空。 + range: 标题最多 150 个字符 + body: + label: 内容 + msg: + empty: 内容不能为空。 + tags: + label: 标签 + msg: + empty: 必须选择一个标签 + answer: + label: 回答内容 + msg: + empty: 回答内容不能为空 + edit_summary: + label: 编辑备注 + placeholder: >- + 简单描述更改原因(更正拼写、修复语法、改进格式) + btn_post_question: 提交问题 + btn_save_edits: 保存更改 + answer_question: 回答自己的问题 + post_question&answer: 提交问题和回答 + tag_selector: + add_btn: 添加标签 + create_btn: 创建新标签 + search_tag: 搜索标签 + hint: "描述您的问题是关于什么,至少需要一个标签。" + no_result: 没有匹配的标签 + tag_required_text: 必选标签(至少一个) + header: + nav: + question: 问题 + tag: 标签 + user: 用户 + profile: 用户主页 + setting: 账号设置 + logout: 退出 + admin: 后台管理 + review: 审查 + bookmark: 收藏夹 + moderation: 管理 + search: + placeholder: 搜索 + footer: + build_on: >- + 由 <1>Apache Answer 提供动力 - 驱动问答社区的开源软件。
用爱制造 © {{cc}}. + upload_img: + name: 更改 + loading: 加载中... + pic_auth_code: + title: 验证码 + placeholder: 输入图片中的文字 + msg: + empty: 验证码不能为空。 + inactive: + first: >- + 就差一步!我们发送了一封激活邮件到 {{mail}}。请按照邮件中的说明激活你的账户。 + info: "如果没有收到,请检查你的垃圾邮件文件夹。" + another: >- + 我们向你的邮箱 {{mail}} 发送了另一封激活电子邮件。可能需要几分钟才能到达;请务必检查您的垃圾邮件箱。 + btn_name: 重新发送激活邮件 + change_btn_name: 更改邮箱 + msg: + empty: 不能为空。 + resend_email: + url_label: 确定要重新发送激活邮件吗? + url_text: 你也可以将上面的激活链接给该用户。 + login: + login_to_continue: 登录以继续 + info_sign: 没有账户?<1>注册 + info_login: 已经有账户?<1>登录 + agreements: 登录即表示您同意<1>隐私政策和<3>服务条款。 + forgot_pass: 忘记密码? + name: + label: 名字 + msg: + empty: 名字不能为空 + range: 名字应该在 4 到 30 个字符之间 + character: '只能由 "a-z"、"A-Z"、"0-9"、" - . _" 组成' + email: + label: 邮箱 + msg: + empty: 邮箱不能为空 + password: + label: 密码 + msg: + empty: 密码不能为空 + different: 两次输入密码不一致 + account_forgot: + page_title: 忘记密码 + btn_name: 发送恢复邮件 + send_success: >- + 如果存在邮箱为 {{mail}} 账户,你将很快收到一封重置密码的说明邮件。 + email: + label: 邮箱 + msg: + empty: 邮箱不能为空 + change_email: + btn_cancel: 取消 + btn_update: 更新电子邮件地址 + send_success: >- + 如果存在邮箱为 {{mail}} 的账户,你将很快收到一封重置密码的说明邮件。 + email: + label: 新的电子邮件地址 + msg: + empty: 邮箱不能为空。 + oauth: + connect: 连接到 {{ auth_name }} + remove: 移除 {{ auth_name }} + oauth_bind_email: + subtitle: 向你的账户添加恢复邮件地址。 + btn_update: 更新电子邮件地址 + email: + label: 邮箱 + msg: + empty: 邮箱不能为空。 + modal_title: 邮箱已经存在。 + modal_content: 该电子邮件地址已经注册。你确定要连接到已有账户吗? + modal_cancel: 更改邮箱 + modal_confirm: 连接到已有账户 + password_reset: + page_title: 密码重置 + btn_name: 重置我的密码 + reset_success: >- + 你已经成功更改密码;你将被重定向到登录页面。 + link_invalid: >- + 抱歉,此密码重置链接已失效。也许是你已经重置过密码了? + to_login: 前往登录页面 + password: + label: 密码 + msg: + empty: 密码不能为空。 + length: 密码长度在8-32个字符之间 + different: 两次输入密码不一致 + password_confirm: + label: 确认新密码 + settings: + page_title: 设置 + goto_modify: 前往修改 + nav: + profile: 我的资料 + notification: 通知 + account: 账号 + interface: 界面 + profile: + heading: 个人资料 + btn_name: 保存 + display_name: + label: 显示名称 + msg: 昵称不能为空。 + msg_range: 显示名称不能超过 30 个字符。 + username: + label: 用户名 + caption: 用户可以通过 "@用户名" 来提及你。 + msg: 用户名不能为空 + msg_range: 用户名不能超过 30 个字符。 + character: '只能由 "a-z"、"A-Z"、"0-9"、" - . _" 组成' + avatar: + label: 头像 + gravatar: Gravatar + gravatar_text: 你可以更改图像在 + custom: 自定义 + custom_text: 你可以上传你的图片。 + default: 系统 + msg: 请上传头像 + bio: + label: 关于我 + website: + label: 网站 + placeholder: "https://example.com" + msg: 网址格式不正确 + location: + label: 位置 + placeholder: "城市,国家" + notification: + heading: 邮件通知 + turn_on: 开启 + inbox: + label: 收件箱通知 + description: 你的提问有新的回答,评论,邀请回答和其他。 + all_new_question: + label: 所有新问题 + description: 获取所有新问题的通知。每周最多有50个问题。 + all_new_question_for_following_tags: + label: 所有关注标签的新问题 + description: 获取关注的标签下新问题通知。 + account: + heading: 账号 + change_email_btn: 更改邮箱 + change_pass_btn: 更改密码 + change_email_info: >- + 邮件已发送。请根据指引完成验证。 + email: + label: 电子邮件地址 + new_email: + label: 新的电子邮件地址 + msg: 新邮箱不能为空。 + pass: + label: 当前密码 + msg: 密码不能为空。 + password_title: 密码 + current_pass: + label: 当前密码 + msg: + empty: 当前密码不能为空 + length: 密码长度必须在 8 至 32 之间 + different: 两次输入的密码不匹配 + new_pass: + label: 新密码 + pass_confirm: + label: 确认新密码 + interface: + heading: 界面 + lang: + label: 界面语言 + text: 设置用户界面语言,在刷新页面后生效。 + my_logins: + title: 我的登录 + label: 使用这些账户登录或注册本网站。 + modal_title: 移除登录 + modal_content: 你确定要从账户里移除该登录? + modal_confirm_btn: 移除 + remove_success: 移除成功 + toast: + update: 更新成功 + update_password: 密码更新成功。 + flag_success: 感谢标记。 + forbidden_operate_self: 禁止对自己执行操作 + review: 您的修订将在审阅通过后显示。 + sent_success: 发送成功 + related_question: + title: 相关问题 + answers: 个回答 + invite_to_answer: + title: 受邀人 + desc: 邀请你认为可能知道答案的人。 + invite: 邀请回答 + add: 添加人员 + search: 搜索人员 + question_detail: + action: 操作 + Asked: 提问于 + asked: 提问于 + update: 修改于 + edit: 编辑于 + commented: 评论 + Views: 阅读次数 + Follow: 关注此问题 + Following: 已关注 + follow_tip: 关注此问题以接收通知 + answered: 回答于 + closed_in: 关闭于 + show_exist: 查看类似问题。 + useful: 有用的 + question_useful: 它是有用和明确的 + question_un_useful: 它不明确或没用的 + question_bookmark: 收藏该问题 + answer_useful: 这是有用的 + answer_un_useful: 它是没有用的 + answers: + title: 个回答 + score: 评分 + newest: 最新 + oldest: 最旧 + btn_accept: 采纳 + btn_accepted: 已被采纳 + write_answer: + title: 你的回答 + edit_answer: 编辑我的回答 + btn_name: 提交你的回答 + add_another_answer: 添加另一个回答 + confirm_title: 继续回答 + continue: 继续 + confirm_info: >- +

你确定要提交一个新的回答吗?

作为替代,你可以通过编辑来完善和改进之前的回答。

+ empty: 回答内容不能为空。 + characters: 内容长度至少 6 个字符。 + tips: + header_1: 感谢你的回答 + li1_1: 请务必确定在 回答问题。提供详细信息并分享你的研究。 + li1_2: 用参考资料或个人经历来支持你所做的任何陈述。 + header_2: 但是 请避免... + li2_1: 请求帮助,寻求澄清,或答复其他答案。 + reopen: + confirm_btn: 重新打开 + title: 重新打开这个帖子 + content: 确定要重新打开吗? + list: + confirm_btn: 列表显示 + title: 列表中显示这个帖子 + content: 确定要列表中显示这个帖子吗? + unlist: + confirm_btn: 列表隐藏 + title: 从列表中隐藏这个帖子 + content: 确定要从列表中隐藏这个帖子吗? + pin: + title: 置顶该帖子 + content: 你确定要全局置顶吗?这个帖子将出现在所有帖子列表的顶部。 + confirm_btn: 置顶 + delete: + title: 删除 + question: >- + 我们不建议 删除有回答的帖子。因为这样做会使得后来的读者无法从该帖子中获得帮助。

如果删除过多有回答的帖子,你的账号将会被禁止提问。你确定要删除吗? + answer_accepted: >- +

我们不建议删除被采纳的回答。因为这样做会使得后来的读者无法从该帖子中获得帮助。

如果删除过多被采纳的回答,你的账号将会被禁止回答任何提问。你确定要删除吗? + other: 你确定要删除? + tip_answer_deleted: 该回答已被删除 + undelete_title: 撤销删除本帖 + undelete_desc: 你确定你要撤销删除吗? + btns: + confirm: 确认 + cancel: 取消 + edit: 编辑 + save: 保存 + delete: 删除 + undelete: 撤消删除 + list: 列表显示 + unlist: 列表隐藏 + unlisted: 已隐藏 + login: 登录 + signup: 注册 + logout: 退出 + verify: 验证 + add_question: 我要提问 + approve: 批准 + reject: 拒绝 + skip: 跳过 + discard_draft: 丢弃草稿 + pinned: 已置顶 + all: 全部 + question: 问题 + answer: 回答 + comment: 评论 + refresh: 刷新 + resend: 重新发送 + deactivate: 取消激活 + active: 激活 + suspend: 封禁 + unsuspend: 解禁 + close: 关闭 + reopen: 重新打开 + ok: 确定 + light: 浅色 + dark: 深色 + system_setting: 跟随系统 + default: 默认 + reset: 重置 + tag: 标签 + post_lowercase: 帖子 + filter: 筛选 + ignore: 忽略 + submit: 提交 + normal: 正常 + closed: 已关闭 + deleted: 已删除 + pending: 等待处理 + more: 更多 + search: + title: 搜索结果 + keywords: 关键词 + options: 选项 + follow: 关注 + following: 已关注 + counts: "{{count}} 个结果" + more: 更多 + sort_btns: + relevance: 相关性 + newest: 最新的 + active: 活跃的 + score: 评分 + more: 更多 + tips: + title: 高级搜索提示 + tag: "<1>[tag] 在指定标签中搜索" + user: "<1>user:username 根据作者搜索" + answer: "<1>answers:0 搜索未回答的问题" + score: "<1>score:3 评分 3+ 的帖子" + question: "<1>is:question 搜索问题" + is_answer: "<1>is:answer 搜索回答" + empty: 找不到任何相关的内容。
请尝试其他关键字,或者减少查找内容的长度。 + share: + name: 分享 + copy: 复制链接 + via: 分享到... + copied: 已复制 + facebook: 分享到 Facebook + twitter: 分享到 Twitter + cannot_vote_for_self: 你不能给自己的帖子投票。 + modal_confirm: + title: 发生错误... + account_result: + success: 你的账号已通过验证,即将返回首页。 + link: 返回首页 + invalid: >- + 抱歉,此验证链接已失效。也许你的账号已经激活了? + confirm_new_email: 你的电子邮箱已更新 + confirm_new_email_invalid: >- + 抱歉,此验证链接已失效。也许是你的邮箱已经成功更改了? + unsubscribe: + page_title: 退订 + success_title: 退订成功 + success_desc: 您已成功退订,并且将不会再收到我们的邮件。 + link: 更改设置 + question: + following_tags: 已关注的标签 + edit: 编辑 + save: 保存 + follow_tag_tip: 关注标签来筛选你的问题列表。 + hot_questions: 热门问题 + all_questions: 全部问题 + x_questions: "{{ count }} 个问题" + x_answers: "{{ count }} 个回答" + questions: 问题 + answers: 回答 + newest: 最新 + active: 活跃 + hot: 热门 + score: 评分 + unanswered: 未回答 + modified: 更新于 + answered: 回答于 + asked: 提问于 + closed: 已关闭 + follow_a_tag: 关注一个标签 + more: 更多 + personal: + overview: 概览 + answers: 回答 + answer: 回答 + questions: 问题 + question: 问题 + bookmarks: 收藏 + reputation: 声望 + comments: 评论 + votes: 得票 + newest: 最新 + score: 评分 + edit_profile: 编辑资料 + visited_x_days: "已访问 {{ count }} 天" + viewed: 浏览次数 + joined: 加入于 + last_login: 上次登录 + about_me: 关于我 + about_me_empty: "// Hello, World!" + top_answers: 高分回答 + top_questions: 高分问题 + stats: 状态 + list_empty: 没有找到相关的内容。
试试看其他选项卡? + accepted: 已采纳 + answered: 回答于 + asked: 提问于 + downvoted: 点踩 + mod_short: 版主 + mod_long: 版主 + x_reputation: 声望 + x_votes: 得票 + x_answers: 个回答 + x_questions: 个问题 + install: + title: 安装 + next: 下一步 + done: 完成 + config_yaml_error: 无法创建 config.yaml 文件。 + lang: + label: 请选择一种语言 + db_type: + label: 数据库引擎 + db_username: + label: 用户名 + placeholder: root + msg: 用户名不能为空 + db_password: + label: 密码 + placeholder: root + msg: 密码不能为空 + db_host: + label: 数据库主机 + placeholder: "db:3306" + msg: 数据库地址不能为空 + db_name: + label: 数据库名 + placeholder: 回答 + msg: 数据库名称不能为空。 + db_file: + label: 数据库文件 + placeholder: /data/answer.db + msg: 数据库文件不能为空。 + config_yaml: + title: 创建 config.yaml + label: 已创建 config.yaml 文件。 + desc: >- + 你可以手动在 <1>/var/wwww/xxx/ 目录中创建 <1>config.yaml 文件并粘贴以下文本。 + info: 完成后,点击“下一步”按钮。 + site_information: 站点信息 + admin_account: 管理员账号 + site_name: + label: 站点名称 + msg: 站点名称不能为空。 + msg_max_length: 站点名称长度不得超过 30 个字符。 + site_url: + label: 网站网址 + text: 此网站的网址。 + msg: + empty: 网址不能为空。 + incorrect: 网址格式不正确。 + max_length: 网址长度不得超过 512 个字符。 + contact_email: + label: 联系邮箱 + text: 负责本网站的主要联系人的电子邮件地址。 + msg: + empty: 联系人邮箱不能为空。 + incorrect: 联系人邮箱地址不正确。 + login_required: + label: 私有的 + switch: 需要登录 + text: 只有登录用户才能访问这个社区。 + admin_name: + label: 名字 + msg: 名字不能为空。 + character: '只能由 "a-z", "0-9", " - . _" 组成' + msg_max_length: 名字长度不能超过 30 个字符。 + admin_password: + label: 密码 + text: >- + 您需要此密码才能登录。请将其存储在一个安全的位置。 + msg: 密码不能为空。 + msg_min_length: 密码必须至少 8 个字符长。 + msg_max_length: 密码长度不能超过 32 个字符。 + admin_email: + label: 邮箱 + text: 您需要此电子邮件才能登录。 + msg: + empty: 邮箱不能为空。 + incorrect: 邮箱格式不正确。 + ready_title: 您的网站已准备好 + ready_desc: >- + 如果你想改变更多的设置,请访问 <1>管理区域;在网站菜单中找到它。 + good_luck: "玩得愉快,祝你好运!" + warn_title: 警告 + warn_desc: >- + 文件 <1>config.yaml 已存在。如果你要重置该文件中的任何配置项,请先删除它。 + install_now: 您可以尝试 <1>现在安装。 + installed: 已安裝 + installed_desc: >- + 你似乎已经安装过了。如果要重新安装,请先清除旧的数据库表。 + db_failed: 数据连接异常! + db_failed_desc: >- + 这或者意味着数据库信息在 <1>config.yaml 文件不正确,或者无法与数据库服务器建立联系。这可能意味着你的主机数据库服务器故障。 + counts: + views: 次浏览 + votes: 个点赞 + answers: 个回答 + accepted: 已被采纳 + page_error: + http_error: HTTP 错误 {{ code }} + desc_403: 您无权访问此页面。 + desc_404: 很抱歉,此页面不存在。 + desc_50X: 服务器遇到了一个错误,无法完成你的请求。 + back_home: 返回首页 + page_maintenance: + desc: "我们正在进行维护,我们将很快回来。" + nav_menus: + dashboard: 后台管理 + contents: 内容管理 + questions: 问题 + answers: 回答 + users: 用户管理 + flags: 举报管理 + settings: 站点设置 + general: 一般 + interface: 界面 + smtp: SMTP + branding: 品牌 + legal: 法律条款 + write: 撰写 + tos: 服务条款 + privacy: 隐私政策 + seo: SEO + customize: 自定义 + themes: 主题 + css_html: CSS/HTML + login: 登录 + privileges: 特权 + plugins: 插件 + installed_plugins: 已安装插件 + website_welcome: 欢迎来到 {{site_name}} + user_center: + login: 登录 + qrcode_login_tip: 请使用 {{ agentName }} 扫描二维码并登录。 + login_failed_email_tip: 登录失败,请允许此应用访问您的邮箱信息,然后重试。 + admin: + admin_header: + title: 后台管理 + dashboard: + title: 后台管理 + welcome: 欢迎来到管理后台! + site_statistics: 站点统计 + questions: "问题:" + answers: "回答:" + comments: "评论:" + votes: "投票:" + users: "用户:" + flags: "举报:" + reviews: "审查:" + site_health: 网站健康 + version: "版本" + https: "HTTPS:" + upload_folder: "上传文件夹:" + run_mode: "运行模式:" + private: 私有 + public: 公开 + smtp: "SMTP:" + timezone: "时区:" + system_info: 系统信息 + go_version: "Go版本:" + database: "数据库:" + database_size: "数据库大小:" + storage_used: "已用存储空间:" + uptime: "运行时间:" + links: 链接 + plugins: 插件 + github: GitHub + blog: 博客 + contact: 联系 + forum: 论坛 + documents: 文档 + feedback: 用户反馈 + support: 帮助 + review: 审查 + config: 配置 + update_to: 更新到 + latest: 最新版本 + check_failed: 校验失败 + "yes": "是" + "no": "否" + not_allowed: 拒绝 + allowed: 允许 + enabled: 已启用 + disabled: 停用 + writable: 可写 + not_writable: 不可写 + flags: + title: 举报 + pending: 等待处理 + completed: 已完成 + flagged: 被举报内容 + flagged_type: 标记了 {{ type }} + created: 创建于 + action: 操作 + review: 审查 + user_role_modal: + title: 更改用户状态为... + btn_cancel: 取消 + btn_submit: 提交 + new_password_modal: + title: 设置新密码 + form: + fields: + password: + label: 密码 + text: 用户将被退出,需要再次登录。 + msg: 密码的长度必须是8-32个字符。 + btn_cancel: 取消 + btn_submit: 提交 + edit_profile_modal: + title: 编辑资料 + form: + fields: + username: + label: 用户名 + msg_range: 用户名不能超过 30 个字符。 + email: + label: 电子邮件地址 + msg_invalid: 无效的邮箱地址 + edit_success: 修改成功 + btn_cancel: 取消 + btn_submit: 提交 + user_modal: + title: 添加新用户 + form: + fields: + users: + label: 批量添加用户 + placeholder: "John Smith, john@example.com, BUSYopr2\nAlice, alice@example.com, fpDntV8q" + text: 用逗号分隔“name, email, password”,每行一个用户。 + msg: "请输入用户的邮箱,每行一个。" + display_name: + label: 显示名称 + msg: 显示名称长度必须为 4-30 个字符 + email: + label: 邮箱 + msg: 邮箱无效。 + password: + label: 密码 + msg: 密码的长度必须是8-32个字符。 + btn_cancel: 取消 + btn_submit: 提交 + users: + title: 用户 + name: 名称 + email: 邮箱 + reputation: 声望 + created_at: 创建时间 + delete_at: 删除时间 + suspend_at: 封禁时间 + status: 状态 + role: 角色 + action: 操作 + change: 更改 + all: 全部 + staff: 工作人员 + more: 更多 + inactive: 不活跃 + suspended: 已封禁 + deleted: 已删除 + normal: 正常 + Moderator: 版主 + Admin: 管理员 + User: 用户 + filter: + placeholder: "按名称筛选,用户:id" + set_new_password: 设置新密码 + edit_profile: 编辑资料 + change_status: 更改状态 + change_role: 更改角色 + show_logs: 显示日志 + add_user: 添加用户 + deactivate_user: + title: 停用用户 + content: 未激活的用户必须重新验证他们的邮箱。 + delete_user: + title: 删除此用户 + content: 确定要删除此用户?此操作无法撤销! + remove: 移除内容 + label: 删除所有问题、 答案、 评论等 + text: 如果你只想删除用户账户,请不要选中此项。 + suspend_user: + title: 挂起此用户 + content: 被封禁的用户将无法登录。 + questions: + page_title: 问题 + unlisted: 已隐藏 + post: 标题 + votes: 得票数 + answers: 回答数 + created: 创建于 + status: 状态 + action: 操作 + change: 更改 + pending: 等待处理 + filter: + placeholder: "按标题过滤,问题:id" + answers: + page_title: 回答 + post: 标题 + votes: 得票数 + created: 创建于 + status: 状态 + action: 操作 + change: 更改 + filter: + placeholder: "按标题筛选,答案:id" + general: + page_title: 一般 + name: + label: 站点名称 + msg: 不能为空 + text: "站点的名称,作为站点的标题。" + site_url: + label: 网站网址 + msg: 网站网址不能为空。 + validate: 请输入一个有效的 URL。 + text: 此网站的地址。 + short_desc: + label: 简短站点描述 + msg: 简短网站描述不能为空。 + text: "简短的标语,作为网站主页的标题(Html 的 title 标签)。" + desc: + label: 站点描述 + msg: 网站描述不能为空。 + text: "使用一句话描述本站,作为网站的描述(Html 的 meta 标签)。" + contact_email: + label: 联系邮箱 + msg: 联系人邮箱不能为空。 + validate: 联系人邮箱无效。 + text: 本网站的主要联系邮箱地址。 + check_update: + label: 软件更新 + text: 自动检查软件更新 + interface: + page_title: 界面 + language: + label: 界面语言 + msg: 不能为空 + text: 设置用户界面语言,在刷新页面后生效。 + time_zone: + label: 时区 + msg: 时区不能为空。 + text: 选择一个与您相同时区的城市。 + smtp: + page_title: SMTP + from_email: + label: 发件人邮箱 + msg: 发件人邮箱不能为空。 + text: 用于发送邮件的地址。 + from_name: + label: 发件人 + msg: 不能为空 + text: 发件人的名字。 + smtp_host: + label: SMTP 主机 + msg: 不能为空 + text: 邮件服务器 + encryption: + label: 加密 + msg: 不能为空 + text: 对于大多数服务器而言,SSL 是推荐开启的。 + ssl: SSL + tls: TLS + none: 无加密 + smtp_port: + label: SMTP 端口 + msg: SMTP 端口必须在 1 ~ 65535 之间。 + text: 邮件服务器的端口号。 + smtp_username: + label: SMTP 用户名 + msg: 不能为空 + smtp_password: + label: SMTP 密码 + msg: 不能为空 + test_email_recipient: + label: 测试收件邮箱 + text: 提供用于接收测试邮件的邮箱地址。 + msg: 测试收件邮箱无效 + smtp_authentication: + label: 启用身份验证 + title: SMTP 身份验证 + msg: 不能为空 + "yes": "是" + "no": "否" + branding: + page_title: 品牌 + logo: + label: 网站标志(Logo) + msg: 图标不能为空。 + text: 在你的网站左上方的Logo图标。使用一个高度为56,长宽比大于3:1的宽长方形图像。如果留空,将显示网站标题文本。 + mobile_logo: + label: 移动端 Logo + text: 在你的网站的移动版上使用的标志。使用一个高度为56的宽矩形图像。如果留空,将使用 "Logo"设置中的图像。 + square_icon: + label: 方形图标 + msg: 方形图标不能为空。 + text: 用作元数据图标的基础的图像。最好是大于512x512。 + favicon: + label: 收藏夹图标 + text: 网站的图标。要在 CDN 正常工作,它必须是 png。 将调整大小到32x32。如果留空,将使用“方形图标”。 + legal: + page_title: 法律条款 + terms_of_service: + label: 服务条款 + text: "您可以在此添加服务内容的条款。如果您已经在别处托管了文档,请在这里提供完整的URL。" + privacy_policy: + label: 隐私政策 + text: "您可以在此添加隐私政策内容。如果您已经在别处托管了文档,请在这里提供完整的URL。" + write: + page_title: 编辑 + restrict_answer: + title: 限制一个回答 + label: 每个用户对于每个问题只能有一个回答 + text: "用户可以使用编辑按钮优化已有的回答" + recommend_tags: + label: 推荐标签 + text: "请在上方输入标签固定链接,每行一个标签。" + required_tag: + title: 必需的标签 + label: 根据需要设置推荐标签 + text: "每个新问题必须至少有一个推荐标签。" + reserved_tags: + label: 保留标签 + text: "保留的标签只能由版主添加到一个帖子中。" + seo: + page_title: 搜索引擎优化 + permalink: + label: 固定链接 + text: 自定义URL结构可以提高可用性,以及你的链接的向前兼容性。 + robots: + label: robots.txt + text: 这将永久覆盖任何相关的网站设置。 + themes: + page_title: 主题 + themes: + label: 主题 + text: 选择一个现有主题。 + color_scheme: + label: 配色方案 + navbar_style: + label: 导航栏样式 + primary_color: + label: 主色调 + text: 修改您主题使用的颜色 + css_and_html: + page_title: CSS 与 HTML + custom_css: + label: 自定义 CSS + text: > + + head: + label: 头部 + text: > + + header: + label: 页眉 + text: > + + footer: + label: 页脚 + text: 这将在 之前插入. + sidebar: + label: 侧边栏 + text: 这将插入侧边栏中。 + login: + page_title: 登录 + membership: + title: 会员 + label: 允许新注册 + text: 关闭以防止任何人创建新账户。 + email_registration: + title: 邮箱注册 + label: 允许邮箱注册 + text: 关闭以阻止任何人通过邮箱创建新账户。 + allowed_email_domains: + title: 允许的邮箱域 + text: 允许注册账户的邮箱域。每行一个域名。留空时忽略。 + private: + title: 非公开的 + label: 需要登录 + text: 只有登录用户才能访问这个社区。 + password_login: + title: 密码登录 + label: 允许使用邮箱和密码登录 + text: "警告:如果您未配置过其他登录方式,关闭密码登录后您则可能无法登录。" + installed_plugins: + title: 已安装插件 + plugin_link: 插件扩展功能。您可以在<1>插件仓库中找到插件。 + filter: + all: 全部 + active: 已启用 + inactive: 未启用 + outdated: 已过期 + plugins: + label: 插件 + text: 选择一个现有的插件。 + name: 名称 + version: 版本 + status: 状态 + action: 操作 + deactivate: 停用 + activate: 启用 + settings: 设置 + settings_users: + title: 用户 + avatar: + label: 默认头像 + text: 没有自定义头像的用户。 + gravatar_base_url: + label: Gravatar 根路径 URL + text: Gravatar 提供商的 API 基础的 URL。当为空时忽略。 + profile_editable: + title: 个人资料可编辑 + allow_update_display_name: + label: 允许用户修改显示名称 + allow_update_username: + label: 允许用户修改用户名 + allow_update_avatar: + label: 允许用户修改个人头像 + allow_update_bio: + label: 允许用户修改个人介绍 + allow_update_website: + label: 允许用户修改个人主页网址 + allow_update_location: + label: 允许用户更改位置 + privilege: + title: 特权 + level: + label: 级别所需声望 + text: 选择特权所需的声望值 + msg: + should_be_number: 输入必须是数字 + number_larger_1: 数字应该大于等于 1 + form: + optional: (选填) + empty: 不能为空 + invalid: 是无效的 + btn_submit: 保存 + not_found_props: "所需属性 {{ key }} 未找到。" + select: 选择 + page_review: + review: 评论 + proposed: 提案 + question_edit: 问题编辑 + answer_edit: 回答编辑 + tag_edit: '标签管理: 编辑标签' + edit_summary: 编辑备注 + edit_question: 编辑问题 + edit_answer: 编辑回答 + edit_tag: 编辑标签 + empty: 没有剩余的审核任务。 + approve_revision_tip: 您是否批准此修订? + approve_flag_tip: 您是否批准此举报? + approve_post_tip: 您是否批准此帖子? + approve_user_tip: 您是否批准此修订? + suggest_edits: 建议的编辑 + flag_post: 举报帖子 + flag_user: 举报用户 + queued_post: 排队的帖子 + queued_user: 排队用户 + filter_label: 类型 + reputation: 声望值 + flag_post_type: 举报这个帖子的类型是 {{ type }} + flag_user_type: 举报这个用户的类型是 {{ type }} + edit_post: 编辑帖子 + list_post: 文章列表 + unlist_post: 隐藏的帖子 + timeline: + undeleted: 取消删除 + deleted: 删除 + downvote: 反对 + upvote: 点赞 + accept: 采纳 + cancelled: 已取消 + commented: '评论:' + rollback: 回滚 + edited: 最后编辑于 + answered: 回答于 + asked: 提问于 + closed: 关闭 + reopened: 重新开启 + created: 创建于 + pin: 已置顶 + unpin: 取消置頂 + show: 已显示 + hide: 已隐藏 + title: "历史记录" + tag_title: "时间线" + show_votes: "显示投票" + n_or_a: N/A + title_for_question: "时间线" + title_for_answer: "{{ title }} 的 {{ author }} 回答时间线" + title_for_tag: "时间线" + datetime: 日期时间 + type: 类型 + by: 由 + comment: 评论 + no_data: "空空如也" + users: + title: 用户 + users_with_the_most_reputation: 本周声望最高的用户 + users_with_the_most_vote: 本周投票最多的用户 + staffs: 我们的社区工作人员 + reputation: 声望值 + votes: 投票 + prompt: + leave_page: 确定要离开此页面? + changes_not_save: 您的更改尚未保存 + draft: + discard_confirm: 您确定要丢弃您的草稿吗? + messages: + post_deleted: 该帖子已被删除。 + post_pin: 该帖子已被置顶。 + post_unpin: 该帖子已被取消置顶。 + post_hide_list: 此帖子已经从列表中隐藏。 + post_show_list: 该帖子已显示到列表中。 + post_reopen: 这个帖子已被重新打开. + post_list: 这个帖子已经被显示 + post_unlist: 这个帖子已经被隐藏 + post_pending: 您的帖子正在等待审核。它将在它获得批准后可见。 From f660b6368ddc8c909c0d08d5a54334d7d30e734b Mon Sep 17 00:00:00 2001 From: kumfo Date: Mon, 19 Aug 2024 15:22:06 +0800 Subject: [PATCH 17/64] fix(dev): delete test file --- dev/i18n/zh_CN.yaml | 2035 ------------------------------------------- 1 file changed, 2035 deletions(-) delete mode 100644 dev/i18n/zh_CN.yaml diff --git a/dev/i18n/zh_CN.yaml b/dev/i18n/zh_CN.yaml deleted file mode 100644 index 6613bfc22..000000000 --- a/dev/i18n/zh_CN.yaml +++ /dev/null @@ -1,2035 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -# The following fields are used for back-end -backend: - base: - success: - other: 成功。 - unknown: - other: 未知错误。 - request_format_error: - other: 请求格式错误。 - unauthorized_error: - other: 未授权。 - database_error: - other: 数据服务器错误。 - forbidden_error: - other: 禁止访问。 - duplicate_request_error: - other: 重复提交。 - action: - report: - other: 举报 - edit: - other: 编辑 - delete: - other: 删除 - close: - other: 关闭 - reopen: - other: 重新打开 - forbidden_error: - other: 禁止访问。 - pin: - other: 置顶 - hide: - other: 列表隐藏 - unpin: - other: 取消置顶 - show: - other: 列表显示 - invite_someone_to_answer: - other: 编辑 - undelete: - other: 撤消删除 - role: - name: - user: - other: 用户 - admin: - other: 管理员 - moderator: - other: 版主 - description: - user: - other: 默认没有特殊权限。 - admin: - other: 拥有管理网站的全部权限。 - moderator: - other: 拥有除访问后台管理以外的所有权限。 - privilege: - level_1: - description: - other: 级别 1(少量声望要求,适合私有团队、群组) - level_2: - description: - other: 级别 2(低声望要求,适合初启动的社区) - level_3: - description: - other: 级别 3(高声望要求,适合成熟的社区) - level_custom: - description: - other: 自定义等级 - rank_question_add_label: - other: 提问 - rank_answer_add_label: - other: 写答案 - rank_comment_add_label: - other: 写评论 - rank_report_add_label: - other: 举报 - rank_comment_vote_up_label: - other: 点赞评论 - rank_link_url_limit_label: - other: 每次发布超过 2 个链接 - rank_question_vote_up_label: - other: 点赞问题 - rank_answer_vote_up_label: - other: 点赞答案 - rank_question_vote_down_label: - other: 点踩问题 - rank_answer_vote_down_label: - other: 点踩答案 - rank_invite_someone_to_answer_label: - other: 邀请回答 - rank_tag_add_label: - other: 创建新标签 - rank_tag_edit_label: - other: 编辑标签描述(需要审核) - rank_question_edit_label: - other: 编辑别人的问题(需要审核) - rank_answer_edit_label: - other: 编辑别人的答案(需要审核) - rank_question_edit_without_review_label: - other: 编辑别人的问题无需审核 - rank_answer_edit_without_review_label: - other: 编辑别人的答案无需审核 - rank_question_audit_label: - other: 审核问题编辑 - rank_answer_audit_label: - other: 审核回答编辑 - rank_tag_audit_label: - other: 审核标签编辑 - rank_tag_edit_without_review_label: - other: 编辑标签描述无需审核 - rank_tag_synonym_label: - other: 管理标签同义词 - email: - other: 邮箱 - e_mail: - other: 邮箱 - password: - other: 密码 - pass: - other: 密码 - original_text: - other: 本帖 - email_or_password_wrong_error: - other: 邮箱和密码不匹配。 - error: - common: - invalid_url: - other: 无效的 URL。 - password: - space_invalid: - other: 密码不得含有空格。 - admin: - cannot_update_their_password: - other: 你无法修改自己的密码。 - cannot_edit_their_profile: - other: 您不能修改您的个人资料。 - cannot_modify_self_status: - other: 你无法修改自己的状态。 - email_or_password_wrong: - other: 邮箱和密码不匹配。 - answer: - not_found: - other: 没有找到答案。 - cannot_deleted: - other: 没有删除权限。 - cannot_update: - other: 没有更新权限。 - question_closed_cannot_add: - other: 问题已关闭,无法添加。 - comment: - edit_without_permission: - other: 不允许编辑评论。 - not_found: - other: 评论未找到。 - cannot_edit_after_deadline: - other: 评论时间太久,无法修改。 - email: - duplicate: - other: 邮箱已存在。 - need_to_be_verified: - other: 邮箱需要验证。 - verify_url_expired: - other: 邮箱验证的网址已过期,请重新发送邮件。 - illegal_email_domain_error: - other: 此邮箱不在允许注册的邮箱域中。请使用其他邮箱尝试。 - lang: - not_found: - other: 语言文件未找到。 - object: - captcha_verification_failed: - other: 验证码错误。 - disallow_follow: - other: 你不能关注。 - disallow_vote: - other: 你不能投票。 - disallow_vote_your_self: - other: 你不能为自己的帖子投票。 - not_found: - other: 对象未找到。 - verification_failed: - other: 验证失败。 - email_or_password_incorrect: - other: 邮箱和密码不匹配。 - old_password_verification_failed: - other: 旧密码验证失败。 - new_password_same_as_previous_setting: - other: 新密码和旧密码相同。 - already_deleted: - other: 该帖子已被删除。 - meta: - object_not_found: - other: Meta 对象未找到 - question: - already_deleted: - other: 该帖子已被删除。 - under_review: - other: 您的帖子正在等待审核。它将在它获得批准后可见。 - not_found: - other: 问题未找到。 - cannot_deleted: - other: 没有删除权限。 - cannot_close: - other: 没有关闭权限。 - cannot_update: - other: 没有更新权限。 - rank: - fail_to_meet_the_condition: - other: 声望值未达到要求。 - vote_fail_to_meet_the_condition: - other: 感谢投票。你至少需要 {{.Rank}} 声望才能投票。 - no_enough_rank_to_operate: - other: 你至少需要 {{.Rank}} 声望才能执行此操作。 - report: - handle_failed: - other: 报告处理失败。 - not_found: - other: 报告未找到。 - tag: - already_exist: - other: 标签已存在。 - not_found: - other: 标签未找到。 - recommend_tag_not_found: - other: 推荐标签不存在。 - recommend_tag_enter: - other: 请选择至少一个必选标签。 - not_contain_synonym_tags: - other: 不应包含同义词标签。 - cannot_update: - other: 没有更新权限。 - is_used_cannot_delete: - other: 你不能删除这个正在使用的标签。 - cannot_set_synonym_as_itself: - other: 你不能将当前标签设为自己的同义词。 - smtp: - config_from_name_cannot_be_email: - other: 发件人名称不能是邮箱地址。 - theme: - not_found: - other: 主题未找到。 - revision: - review_underway: - other: 目前无法编辑,有一个版本在审阅队列中。 - no_permission: - other: 无权限修改。 - user: - external_login_missing_user_id: - other: 第三方平台没有提供唯一的 UserID,所以你不能登录,请联系网站管理员。 - external_login_unbinding_forbidden: - other: 请在移除此登录之前为你的账户设置登录密码。 - email_or_password_wrong: - other: - other: 邮箱和密码不匹配。 - not_found: - other: 用户未找到。 - suspended: - other: 用户已被封禁。 - username_invalid: - other: 用户名无效。 - username_duplicate: - other: 用户名已被使用。 - set_avatar: - other: 头像设置错误。 - cannot_update_your_role: - other: 你不能修改自己的角色。 - not_allowed_registration: - other: 该网站暂未开放注册。 - not_allowed_login_via_password: - other: 该网站暂不支持密码登录。 - access_denied: - other: 拒绝访问 - page_access_denied: - other: 您没有权限访问此页面。 - add_bulk_users_format_error: - other: "发生错误,{{.Field}} 格式错误,在 '{{.Content}}' 行数 {{.Line}}. {{.ExtraMessage}}" - add_bulk_users_amount_error: - other: "一次性添加的用户数量应在 1-{{.MaxAmount}} 之间。" - config: - read_config_failed: - other: 读取配置失败 - database: - connection_failed: - other: 数据库连接失败 - create_table_failed: - other: 创建表失败 - install: - create_config_failed: - other: 无法创建 config.yaml 文件。 - upload: - unsupported_file_format: - other: 不支持的文件格式。 - site_info: - config_not_found: - other: 未找到网站的该配置信息。 - reason: - spam: - name: - other: 垃圾信息 - desc: - other: 这个帖子是一个广告,或是破坏性行为。它对当前的主题无帮助或无关。 - rude_or_abusive: - name: - other: 粗鲁或辱骂的 - desc: - other: - - 一个有理智的人都会认为这种内容不适合进行尊重性的讨论。 - - 论坛 - a_duplicate: - name: - other: 重复内容 - desc: - other: 该问题有人问过,而且已经有了答案。 - placeholder: - other: 输入已有的问题链接 - not_a_answer: - name: - other: 不是答案 - desc: - other: - - 这张贴作为答案,但它不会试图回答 - - 这可能是一个编辑、一个评论、另一个问题。 - - 或全部删除。 - no_longer_needed: - name: - other: 不再需要 - desc: - other: 该评论已过时,对话性质或与此帖子无关。 - something: - name: - other: 其他原因 - desc: - other: 此帖子需要工作人员注意,因为是上述所列以外的其他理由。 - placeholder: - other: 让我们具体知道你关心的什么 - community_specific: - name: - other: 社区特定原因 - desc: - other: 该问题不符合社区准则。 - not_clarity: - name: - other: 需要细节或澄清 - desc: - other: 该问题目前涵盖多个问题。它应该侧重在一个问题上。 - looks_ok: - name: - other: 看起来没问题 - desc: - other: 这个帖子是好的,不是低质量。 - needs_edit: - name: - other: 需要编辑,我已做了修改。 - desc: - other: 改进和纠正你自己帖子中的问题。 - needs_close: - name: - other: 需要关闭 - desc: - other: 关闭的问题不能回答,但仍然可以编辑、投票和评论。 - needs_delete: - name: - other: 需要删除 - desc: - other: 该帖子将被删除。 - question: - close: - duplicate: - name: - other: 垃圾信息 - desc: - other: 此问题以前就有人问过,而且已经有了答案。 - guideline: - name: - other: 社区特定原因 - desc: - other: 该问题不符合社区准则。 - multiple: - name: - other: 需要细节或澄清 - desc: - other: - - 该问题目前涵盖多个问题。它应该侧重在一个问题上。 - - 只关注一个问题。 - other: - name: - other: 其他原因 - desc: - other: 该帖子存在上面没有列出的另一个原因。 - operation_type: - asked: - other: 提问于 - answered: - other: 回答于 - modified: - other: 修改于 - deleted_title: - other: 删除的问题 - notification: - action: - update_question: - other: 更新了问题 - answer_the_question: - other: 回答了问题 - update_answer: - other: 更新了答案 - accept_answer: - other: 采纳了答案 - comment_question: - other: 评论了问题 - comment_answer: - other: 评论了答案 - reply_to_you: - other: 回复了你 - mention_you: - other: 提到了你 - your_question_is_closed: - other: 你的问题已被关闭 - your_question_was_deleted: - other: 你的问题已被删除 - your_answer_was_deleted: - other: 你的答案已被删除 - your_comment_was_deleted: - other: 你的评论已被删除 - up_voted_question: - other: 点赞问题 - down_voted_question: - other: 点踩问题 - up_voted_answer: - other: 点赞答案 - down_voted_answer: - other: 点踩回答 - up_voted_comment: - other: 点赞评论 - invited_you_to_answer: - other: 邀请你回答 - email_tpl: - change_email: - title: - other: "[{{.SiteName}}] 确认你的新邮箱地址" - body: - other: "请点击以下链接确认你在 {{.SiteName}} 上的新邮箱地址:
\n{{.ChangeEmailUrl}}

\n\n如果你没有请求此更改,请忽略此邮件。\n" - new_answer: - title: - other: "[{{.SiteName}}] {{.DisplayName}} 回答了你的问题" - body: - other: "{{.QuestionTitle}}

\n\n{{.DisplayName}}:
\n
{{.AnswerSummary}}

\n在 {{.SiteName}} 上查看

\n\n--
\n取消订阅" - invited_you_to_answer: - title: - other: "[{{.SiteName}}] {{.DisplayName}} 邀请您回答问题" - body: - other: "{{.QuestionTitle}}

\n\n{{.DisplayName}}:
\n
我想你可能知道答案。

\n在 {{.SiteName}} 上查看

\n\n--
\n取消订阅" - new_comment: - title: - other: "[{{.SiteName}}] {{.DisplayName}} 评论了你的帖子" - body: - other: "{{.QuestionTitle}}

\n\n{{.DisplayName}}:
\n
{{.CommentSummary}}

\n在 {{.SiteName}} 上查看

\n\n--
\n取消订阅" - new_question: - title: - other: "[{{.SiteName}}] 新问题: {{.QuestionTitle}}" - body: - other: "{{.QuestionTitle}}
\n{{.Tags}}

\n\n--
\n取消订阅" - pass_reset: - title: - other: "[{{.SiteName }}] 重置密码" - body: - other: "有人要求在 [{{.SiteName}}] 上重置你的密码。

\n\n如果这不是你的操作,请安心忽略此电子邮件。

\n\n请点击以下链接设置一个新密码:
\n{{.PassResetUrl}}\n" - register: - title: - other: "[{{.SiteName}}] 确认你的新账户" - body: - other: "欢迎加入 {{.SiteName}}!

\n\n请点击以下链接确认并激活你的新账户:
\n{{.RegisterUrl}}

\n\n如果上面的链接不能点击,请将其复制并粘贴到你的浏览器地址栏中。\n" - test: - title: - other: "[{{.SiteName}}] 测试邮件" - body: - other: "这是一封测试邮件。" - action_activity_type: - upvote: - other: 点赞 - upvoted: - other: 点赞 - downvote: - other: 点踩 - downvoted: - other: 点踩 - accept: - other: 采纳 - accepted: - other: 已采纳 - edit: - other: 编辑 - review: - queued_post: - other: 排队的帖子 - flagged_post: - other: 举报的帖子 - suggested_post_edit: - other: 建议的编辑 - reaction: - tooltip: - other: "{{ .Names }} 以及另外 {{ .Count }} 个..." -plugin: - s3_cdn: - backend: - info: - name: - other: S3 存储 CDN - description: - other: 上传文件到S3存储 - config: - endpoint: - title: - other: Endpoint - description: - other: S3存储的Endpoint - bucket_name: - title: - other: Bucket名称 - description: - other: S3存储的Bucket名称 - object_key_prefix: - title: - other: 对象Key前缀 - description: - other: 对象键的前缀,如'answer/data/',以'/'结尾 - access_key_id: - title: - other: AccessKeyID - description: - other: S3存储的AccessKeyID - access_key_secret: - title: - other: AccessKeySecret - description: - other: S3存储的AccessKeySecret - access_token: - title: - other: AccessToken - description: - other: 访问 S3 所需的 AccessToken - visit_url_prefix: - title: - other: 访问URL前缀 - description: - other: 上传静态文件CDN最终访问地址的前缀,以 '/' 结尾 https://static.example.com/xxx/ - max_file_size: - title: - other: 文件最大大小(MB) - description: - other: 限制上传文件的最大大小,单位MB,默认为10MB - region: - title: - other: 区域(Region) - description: - other: S3存储区域 - disable_ssl: - title: - other: 禁用SSL - description: - other: 我们建议您使用SSL访问S3存储。如果您想禁用SSL,请选中此选项。 - err: - mis_storage_config: - other: 错误的存储配置导致上传失败 - file_not_found: - other: 文件未找到 - unsupported_file_type: - other: 不支持的文件类型 - over_file_size_limit: - other: 超过文件大小限制 - upload_file_failed: - other: 上传文件失败 - aliyunoss_cdn: - backend: - info: - name: - other: 阿里云CDN OSS存储 - description: - other: 上传文件到阿里云CDN OSS存储 - config: - endpoint: - title: - other: Endpoint - description: - other: 阿里云OSS存储的Endpoint - bucket_name: - title: - other: Bucket名称 - description: - other: 阿里云OSS存储的Bucket名称 - object_key_prefix: - title: - other: 对象Key前缀 - description: - other: 对象键的前缀,如'answer/data/',以'/'结尾 - access_key_id: - title: - other: AccessKeyID - description: - other: 阿里云OSS存储的AccessKeyID - access_key_secret: - title: - other: AccessKeySecret - description: - other: 阿里云OSS存储的AccessKeySecret - visit_url_prefix: - title: - other: 访问URL前缀 - description: - other: CDN最终访问地址的前缀,以 '/' 结尾 https://example.com/xxx/ - max_file_size: - title: - other: 最大文件大小(MB) - description: - other: 限制上传文件的最大大小,单位为MB,默认为 10MB - err: - mis_storage_config: - other: 错误的存储配置导致上传失败 - file_not_found: - other: 文件未找到 - unsupported_file_type: - other: 不支持的文件类型 - over_file_size_limit: - other: 超过文件大小限制 - upload_file_failed: - other: 上传文件失败 -# The following fields are used for interface presentation(Front-end) -ui: - how_to_format: - title: 如何排版 - desc: >- -
  • 添加链接

    <https://url.com>

    [标题](https://url.com)
  • 段落之间使用空行分隔

  • _斜体_ 或者 **粗体**

  • 使用 4 个空格缩进代码

  • 在行首添加 > 表示引用

  • 反引号进行转义 `像 _这样_`

  • 使用 ``` 创建代码块

    ```
    这是代码块
    ```
- pagination: - prev: 上一页 - next: 下一页 - page_title: - question: 问题 - questions: 问题 - tag: 标签 - tags: 标签 - tag_wiki: 标签维基 - create_tag: 创建标签 - edit_tag: 编辑标签 - ask_a_question: 提问题 - edit_question: 编辑问题 - edit_answer: 编辑回答 - search: 搜索 - posts_containing: 帖子包含 - settings: 设置 - notifications: 通知 - login: 登录 - sign_up: 注册 - account_recovery: 账号恢复 - account_activation: 账号激活 - confirm_email: 确认电子邮件 - account_suspended: 账号已被封禁 - admin: 后台管理 - change_email: 修改邮箱 - install: Answer 安装 - upgrade: Answer 升级 - maintenance: 网站维护 - users: 用户 - oauth_callback: 处理中 - http_404: HTTP 错误 404 - http_50X: HTTP 错误 500 - http_403: HTTP 错误 403 - logout: 退出 - notifications: - title: 通知 - inbox: 收件箱 - achievement: 成就 - new_alerts: 新通知 - all_read: 全部标记为已读 - show_more: 显示更多 - someone: 有人 - inbox_type: - all: 全部 - posts: 帖子 - invites: 邀请 - votes: 投票 - suspended: - title: 你的账号账号已被封禁 - until_time: "你的账号被封禁直到 {{ time }}。" - forever: 你的账号已被永久封禁。 - end: 你违反了我们的社区准则。 - contact_us: 联系我们 - editor: - blockquote: - text: 引用 - bold: - text: 粗体 - chart: - text: 图表 - flow_chart: 流程图 - sequence_diagram: 时序图 - class_diagram: 类图 - state_diagram: 状态图 - entity_relationship_diagram: 实体关系图 - user_defined_diagram: 用户自定义图表 - gantt_chart: 甘特图 - pie_chart: 饼图 - code: - text: 代码块 - add_code: 添加代码块 - form: - fields: - code: - label: 代码块 - msg: - empty: 代码块不能为空 - language: - label: 语言 - placeholder: 自动识别 - btn_cancel: 取消 - btn_confirm: 添加 - formula: - text: 公式 - options: - inline: 行内公式 - block: 块级公式 - heading: - text: 标题 - options: - h1: 标题 1 - h2: 标题 2 - h3: 标题 3 - h4: 标题 4 - h5: 标题 5 - h6: 标题 6 - help: - text: 帮助 - hr: - text: 水平线 - image: - text: 图片 - add_image: 添加图片 - tab_image: 上传图片 - form_image: - fields: - file: - label: 图像文件 - btn: 选择图片 - msg: - empty: 请选择图片文件。 - only_image: 只能上传图片文件。 - max_size: 文件大小不能超过 4 MB。 - desc: - label: 描述 - tab_url: 图片地址 - form_url: - fields: - url: - label: 图片地址 - msg: - empty: 图片地址不能为空 - name: - label: 描述 - btn_cancel: 取消 - btn_confirm: 添加 - uploading: 上传中 - indent: - text: 缩进 - outdent: - text: 减少缩进 - italic: - text: 斜体 - link: - text: 超链接 - add_link: 添加超链接 - form: - fields: - url: - label: 链接 - msg: - empty: 链接不能为空。 - name: - label: 描述 - btn_cancel: 取消 - btn_confirm: 添加 - ordered_list: - text: 有序列表 - unordered_list: - text: 无序列表 - table: - text: 表格 - heading: 表头 - cell: 单元格 - close_modal: - title: 关闭原因是... - btn_cancel: 取消 - btn_submit: 提交 - remark: - empty: 不能为空。 - msg: - empty: 请选择一个原因。 - report_modal: - flag_title: 我举报这篇帖子的原因是... - close_title: 我关闭这篇帖子的原因是... - review_question_title: 审查问题 - review_answer_title: 审查回答 - review_comment_title: 审查评论 - btn_cancel: 取消 - btn_submit: 提交 - remark: - empty: 不能为空 - msg: - empty: 请选择一个原因。 - not_a_url: URL 格式不正确。 - url_not_match: URL 来源与当前网站不匹配。 - tag_modal: - title: 创建新标签 - form: - fields: - display_name: - label: 显示名称 - msg: - empty: 显示名称不能为空。 - range: 显示名称不能超过 35 个字符。 - slug_name: - label: URL 固定链接 - desc: URL 固定链接不能超过 35 个字符。 - msg: - empty: URL 固定链接不能为空。 - range: URL 固定链接不能超过 35 个字符。 - character: URL 固定链接包含非法字符。 - desc: - label: 描述 - revision: - label: 编辑历史 - edit_summary: - label: 编辑备注 - placeholder: >- - 简单描述更改原因(更正拼写、修复语法、改进格式) - btn_cancel: 取消 - btn_submit: 提交 - btn_post: 发布新标签 - tag_info: - created_at: 创建于 - edited_at: 编辑于 - history: 历史 - synonyms: - title: 同义词 - text: 以下标签将被重置到 - empty: 此标签目前没有同义词。 - btn_add: 添加同义词 - btn_edit: 编辑 - btn_save: 保存 - synonyms_text: 以下标签将被重置到 - delete: - title: 删除标签 - tip_with_posts: >- -

我们不允许 删除带有帖子的标签

请先从帖子中移除此标签。

- tip_with_synonyms: >- -

我们不允许 删除带有同义词的标签

请先从此标签中删除同义词。

- tip: 确定要删除吗? - close: 关闭 - edit_tag: - title: 编辑标签 - default_reason: 编辑标签 - default_first_reason: 添加标签 - btn_save_edits: 保存更改 - btn_cancel: 取消 - dates: - long_date: MM 月 DD 日 - long_date_with_year: "YYYY 年 MM 月 DD 日" - long_date_with_time: "YYYY 年 MM 月 DD 日 HH:mm" - now: 刚刚 - x_seconds_ago: "{{count}} 秒前" - x_minutes_ago: "{{count}} 分钟前" - x_hours_ago: "{{count}} 小时前" - hour: 小时 - day: 天 - hours: 小时 - days: 日 - reaction: - heart: 爱心 - smile: 微笑 - frown: 愁 - btn_label: 添加或删除回应。 - undo_emoji: 撤销 {{ emoji }} 回应 - react_emoji: 用 {{ emoji }} 回应 - unreact_emoji: 撤销 {{ emoji }} - comment: - btn_add_comment: 添加评论 - reply_to: 回复 - btn_reply: 回复 - btn_edit: 编辑 - btn_delete: 删除 - btn_flag: 举报 - btn_save_edits: 保存更改 - btn_cancel: 取消 - show_more: "{{count}} 条剩余评论" - tip_question: >- - 使用评论提问更多信息或者提出改进意见。避免在评论里回答问题。 - tip_answer: >- - 使用评论对回答者进行回复,或者通知回答者你已更新了问题的内容。如果要补充或者完善问题的内容,请在原问题中更改。 - tip_vote: 它给帖子添加了一些有用的内容 - edit_answer: - title: 编辑回答 - default_reason: 编辑回答 - default_first_reason: 添加答案 - form: - fields: - revision: - label: 编辑历史 - answer: - label: 回答内容 - feedback: - characters: 内容长度至少 6 个字符 - edit_summary: - label: 编辑摘要 - placeholder: >- - 简单描述更改原因(更正拼写、修复语法、改进格式) - btn_save_edits: 保存更改 - btn_cancel: 取消 - tags: - title: 标签 - sort_buttons: - popular: 热门 - name: 名称 - newest: 最新 - button_follow: 关注 - button_following: 已关注 - tag_label: 个问题 - search_placeholder: 通过标签名称过滤 - no_desc: 此标签无描述。 - more: 更多 - ask: - title: 新增问题 - edit_title: 编辑问题 - default_reason: 编辑问题 - default_first_reason: 新增问题 - similar_questions: 相似问题 - form: - fields: - revision: - label: 修订版本 - title: - label: 标题 - placeholder: 请详细描述你的问题,想象你在问一个人 - msg: - empty: 标题不能为空。 - range: 标题最多 150 个字符 - body: - label: 内容 - msg: - empty: 内容不能为空。 - tags: - label: 标签 - msg: - empty: 必须选择一个标签 - answer: - label: 回答内容 - msg: - empty: 回答内容不能为空 - edit_summary: - label: 编辑备注 - placeholder: >- - 简单描述更改原因(更正拼写、修复语法、改进格式) - btn_post_question: 提交问题 - btn_save_edits: 保存更改 - answer_question: 回答自己的问题 - post_question&answer: 提交问题和回答 - tag_selector: - add_btn: 添加标签 - create_btn: 创建新标签 - search_tag: 搜索标签 - hint: "描述您的问题是关于什么,至少需要一个标签。" - no_result: 没有匹配的标签 - tag_required_text: 必选标签(至少一个) - header: - nav: - question: 问题 - tag: 标签 - user: 用户 - profile: 用户主页 - setting: 账号设置 - logout: 退出 - admin: 后台管理 - review: 审查 - bookmark: 收藏夹 - moderation: 管理 - search: - placeholder: 搜索 - footer: - build_on: >- - 由 <1>Apache Answer 提供动力 - 驱动问答社区的开源软件。
用爱制造 © {{cc}}. - upload_img: - name: 更改 - loading: 加载中... - pic_auth_code: - title: 验证码 - placeholder: 输入图片中的文字 - msg: - empty: 验证码不能为空。 - inactive: - first: >- - 就差一步!我们发送了一封激活邮件到 {{mail}}。请按照邮件中的说明激活你的账户。 - info: "如果没有收到,请检查你的垃圾邮件文件夹。" - another: >- - 我们向你的邮箱 {{mail}} 发送了另一封激活电子邮件。可能需要几分钟才能到达;请务必检查您的垃圾邮件箱。 - btn_name: 重新发送激活邮件 - change_btn_name: 更改邮箱 - msg: - empty: 不能为空。 - resend_email: - url_label: 确定要重新发送激活邮件吗? - url_text: 你也可以将上面的激活链接给该用户。 - login: - login_to_continue: 登录以继续 - info_sign: 没有账户?<1>注册 - info_login: 已经有账户?<1>登录 - agreements: 登录即表示您同意<1>隐私政策和<3>服务条款。 - forgot_pass: 忘记密码? - name: - label: 名字 - msg: - empty: 名字不能为空 - range: 名字应该在 4 到 30 个字符之间 - character: '只能由 "a-z"、"A-Z"、"0-9"、" - . _" 组成' - email: - label: 邮箱 - msg: - empty: 邮箱不能为空 - password: - label: 密码 - msg: - empty: 密码不能为空 - different: 两次输入密码不一致 - account_forgot: - page_title: 忘记密码 - btn_name: 发送恢复邮件 - send_success: >- - 如果存在邮箱为 {{mail}} 账户,你将很快收到一封重置密码的说明邮件。 - email: - label: 邮箱 - msg: - empty: 邮箱不能为空 - change_email: - btn_cancel: 取消 - btn_update: 更新电子邮件地址 - send_success: >- - 如果存在邮箱为 {{mail}} 的账户,你将很快收到一封重置密码的说明邮件。 - email: - label: 新的电子邮件地址 - msg: - empty: 邮箱不能为空。 - oauth: - connect: 连接到 {{ auth_name }} - remove: 移除 {{ auth_name }} - oauth_bind_email: - subtitle: 向你的账户添加恢复邮件地址。 - btn_update: 更新电子邮件地址 - email: - label: 邮箱 - msg: - empty: 邮箱不能为空。 - modal_title: 邮箱已经存在。 - modal_content: 该电子邮件地址已经注册。你确定要连接到已有账户吗? - modal_cancel: 更改邮箱 - modal_confirm: 连接到已有账户 - password_reset: - page_title: 密码重置 - btn_name: 重置我的密码 - reset_success: >- - 你已经成功更改密码;你将被重定向到登录页面。 - link_invalid: >- - 抱歉,此密码重置链接已失效。也许是你已经重置过密码了? - to_login: 前往登录页面 - password: - label: 密码 - msg: - empty: 密码不能为空。 - length: 密码长度在8-32个字符之间 - different: 两次输入密码不一致 - password_confirm: - label: 确认新密码 - settings: - page_title: 设置 - goto_modify: 前往修改 - nav: - profile: 我的资料 - notification: 通知 - account: 账号 - interface: 界面 - profile: - heading: 个人资料 - btn_name: 保存 - display_name: - label: 显示名称 - msg: 昵称不能为空。 - msg_range: 显示名称不能超过 30 个字符。 - username: - label: 用户名 - caption: 用户可以通过 "@用户名" 来提及你。 - msg: 用户名不能为空 - msg_range: 用户名不能超过 30 个字符。 - character: '只能由 "a-z"、"A-Z"、"0-9"、" - . _" 组成' - avatar: - label: 头像 - gravatar: Gravatar - gravatar_text: 你可以更改图像在 - custom: 自定义 - custom_text: 你可以上传你的图片。 - default: 系统 - msg: 请上传头像 - bio: - label: 关于我 - website: - label: 网站 - placeholder: "https://example.com" - msg: 网址格式不正确 - location: - label: 位置 - placeholder: "城市,国家" - notification: - heading: 邮件通知 - turn_on: 开启 - inbox: - label: 收件箱通知 - description: 你的提问有新的回答,评论,邀请回答和其他。 - all_new_question: - label: 所有新问题 - description: 获取所有新问题的通知。每周最多有50个问题。 - all_new_question_for_following_tags: - label: 所有关注标签的新问题 - description: 获取关注的标签下新问题通知。 - account: - heading: 账号 - change_email_btn: 更改邮箱 - change_pass_btn: 更改密码 - change_email_info: >- - 邮件已发送。请根据指引完成验证。 - email: - label: 电子邮件地址 - new_email: - label: 新的电子邮件地址 - msg: 新邮箱不能为空。 - pass: - label: 当前密码 - msg: 密码不能为空。 - password_title: 密码 - current_pass: - label: 当前密码 - msg: - empty: 当前密码不能为空 - length: 密码长度必须在 8 至 32 之间 - different: 两次输入的密码不匹配 - new_pass: - label: 新密码 - pass_confirm: - label: 确认新密码 - interface: - heading: 界面 - lang: - label: 界面语言 - text: 设置用户界面语言,在刷新页面后生效。 - my_logins: - title: 我的登录 - label: 使用这些账户登录或注册本网站。 - modal_title: 移除登录 - modal_content: 你确定要从账户里移除该登录? - modal_confirm_btn: 移除 - remove_success: 移除成功 - toast: - update: 更新成功 - update_password: 密码更新成功。 - flag_success: 感谢标记。 - forbidden_operate_self: 禁止对自己执行操作 - review: 您的修订将在审阅通过后显示。 - sent_success: 发送成功 - related_question: - title: 相关问题 - answers: 个回答 - invite_to_answer: - title: 受邀人 - desc: 邀请你认为可能知道答案的人。 - invite: 邀请回答 - add: 添加人员 - search: 搜索人员 - question_detail: - action: 操作 - Asked: 提问于 - asked: 提问于 - update: 修改于 - edit: 编辑于 - commented: 评论 - Views: 阅读次数 - Follow: 关注此问题 - Following: 已关注 - follow_tip: 关注此问题以接收通知 - answered: 回答于 - closed_in: 关闭于 - show_exist: 查看类似问题。 - useful: 有用的 - question_useful: 它是有用和明确的 - question_un_useful: 它不明确或没用的 - question_bookmark: 收藏该问题 - answer_useful: 这是有用的 - answer_un_useful: 它是没有用的 - answers: - title: 个回答 - score: 评分 - newest: 最新 - oldest: 最旧 - btn_accept: 采纳 - btn_accepted: 已被采纳 - write_answer: - title: 你的回答 - edit_answer: 编辑我的回答 - btn_name: 提交你的回答 - add_another_answer: 添加另一个回答 - confirm_title: 继续回答 - continue: 继续 - confirm_info: >- -

你确定要提交一个新的回答吗?

作为替代,你可以通过编辑来完善和改进之前的回答。

- empty: 回答内容不能为空。 - characters: 内容长度至少 6 个字符。 - tips: - header_1: 感谢你的回答 - li1_1: 请务必确定在 回答问题。提供详细信息并分享你的研究。 - li1_2: 用参考资料或个人经历来支持你所做的任何陈述。 - header_2: 但是 请避免... - li2_1: 请求帮助,寻求澄清,或答复其他答案。 - reopen: - confirm_btn: 重新打开 - title: 重新打开这个帖子 - content: 确定要重新打开吗? - list: - confirm_btn: 列表显示 - title: 列表中显示这个帖子 - content: 确定要列表中显示这个帖子吗? - unlist: - confirm_btn: 列表隐藏 - title: 从列表中隐藏这个帖子 - content: 确定要从列表中隐藏这个帖子吗? - pin: - title: 置顶该帖子 - content: 你确定要全局置顶吗?这个帖子将出现在所有帖子列表的顶部。 - confirm_btn: 置顶 - delete: - title: 删除 - question: >- - 我们不建议 删除有回答的帖子。因为这样做会使得后来的读者无法从该帖子中获得帮助。

如果删除过多有回答的帖子,你的账号将会被禁止提问。你确定要删除吗? - answer_accepted: >- -

我们不建议删除被采纳的回答。因为这样做会使得后来的读者无法从该帖子中获得帮助。

如果删除过多被采纳的回答,你的账号将会被禁止回答任何提问。你确定要删除吗? - other: 你确定要删除? - tip_answer_deleted: 该回答已被删除 - undelete_title: 撤销删除本帖 - undelete_desc: 你确定你要撤销删除吗? - btns: - confirm: 确认 - cancel: 取消 - edit: 编辑 - save: 保存 - delete: 删除 - undelete: 撤消删除 - list: 列表显示 - unlist: 列表隐藏 - unlisted: 已隐藏 - login: 登录 - signup: 注册 - logout: 退出 - verify: 验证 - add_question: 我要提问 - approve: 批准 - reject: 拒绝 - skip: 跳过 - discard_draft: 丢弃草稿 - pinned: 已置顶 - all: 全部 - question: 问题 - answer: 回答 - comment: 评论 - refresh: 刷新 - resend: 重新发送 - deactivate: 取消激活 - active: 激活 - suspend: 封禁 - unsuspend: 解禁 - close: 关闭 - reopen: 重新打开 - ok: 确定 - light: 浅色 - dark: 深色 - system_setting: 跟随系统 - default: 默认 - reset: 重置 - tag: 标签 - post_lowercase: 帖子 - filter: 筛选 - ignore: 忽略 - submit: 提交 - normal: 正常 - closed: 已关闭 - deleted: 已删除 - pending: 等待处理 - more: 更多 - search: - title: 搜索结果 - keywords: 关键词 - options: 选项 - follow: 关注 - following: 已关注 - counts: "{{count}} 个结果" - more: 更多 - sort_btns: - relevance: 相关性 - newest: 最新的 - active: 活跃的 - score: 评分 - more: 更多 - tips: - title: 高级搜索提示 - tag: "<1>[tag] 在指定标签中搜索" - user: "<1>user:username 根据作者搜索" - answer: "<1>answers:0 搜索未回答的问题" - score: "<1>score:3 评分 3+ 的帖子" - question: "<1>is:question 搜索问题" - is_answer: "<1>is:answer 搜索回答" - empty: 找不到任何相关的内容。
请尝试其他关键字,或者减少查找内容的长度。 - share: - name: 分享 - copy: 复制链接 - via: 分享到... - copied: 已复制 - facebook: 分享到 Facebook - twitter: 分享到 Twitter - cannot_vote_for_self: 你不能给自己的帖子投票。 - modal_confirm: - title: 发生错误... - account_result: - success: 你的账号已通过验证,即将返回首页。 - link: 返回首页 - invalid: >- - 抱歉,此验证链接已失效。也许你的账号已经激活了? - confirm_new_email: 你的电子邮箱已更新 - confirm_new_email_invalid: >- - 抱歉,此验证链接已失效。也许是你的邮箱已经成功更改了? - unsubscribe: - page_title: 退订 - success_title: 退订成功 - success_desc: 您已成功退订,并且将不会再收到我们的邮件。 - link: 更改设置 - question: - following_tags: 已关注的标签 - edit: 编辑 - save: 保存 - follow_tag_tip: 关注标签来筛选你的问题列表。 - hot_questions: 热门问题 - all_questions: 全部问题 - x_questions: "{{ count }} 个问题" - x_answers: "{{ count }} 个回答" - questions: 问题 - answers: 回答 - newest: 最新 - active: 活跃 - hot: 热门 - score: 评分 - unanswered: 未回答 - modified: 更新于 - answered: 回答于 - asked: 提问于 - closed: 已关闭 - follow_a_tag: 关注一个标签 - more: 更多 - personal: - overview: 概览 - answers: 回答 - answer: 回答 - questions: 问题 - question: 问题 - bookmarks: 收藏 - reputation: 声望 - comments: 评论 - votes: 得票 - newest: 最新 - score: 评分 - edit_profile: 编辑资料 - visited_x_days: "已访问 {{ count }} 天" - viewed: 浏览次数 - joined: 加入于 - last_login: 上次登录 - about_me: 关于我 - about_me_empty: "// Hello, World!" - top_answers: 高分回答 - top_questions: 高分问题 - stats: 状态 - list_empty: 没有找到相关的内容。
试试看其他选项卡? - accepted: 已采纳 - answered: 回答于 - asked: 提问于 - downvoted: 点踩 - mod_short: 版主 - mod_long: 版主 - x_reputation: 声望 - x_votes: 得票 - x_answers: 个回答 - x_questions: 个问题 - install: - title: 安装 - next: 下一步 - done: 完成 - config_yaml_error: 无法创建 config.yaml 文件。 - lang: - label: 请选择一种语言 - db_type: - label: 数据库引擎 - db_username: - label: 用户名 - placeholder: root - msg: 用户名不能为空 - db_password: - label: 密码 - placeholder: root - msg: 密码不能为空 - db_host: - label: 数据库主机 - placeholder: "db:3306" - msg: 数据库地址不能为空 - db_name: - label: 数据库名 - placeholder: 回答 - msg: 数据库名称不能为空。 - db_file: - label: 数据库文件 - placeholder: /data/answer.db - msg: 数据库文件不能为空。 - config_yaml: - title: 创建 config.yaml - label: 已创建 config.yaml 文件。 - desc: >- - 你可以手动在 <1>/var/wwww/xxx/ 目录中创建 <1>config.yaml 文件并粘贴以下文本。 - info: 完成后,点击“下一步”按钮。 - site_information: 站点信息 - admin_account: 管理员账号 - site_name: - label: 站点名称 - msg: 站点名称不能为空。 - msg_max_length: 站点名称长度不得超过 30 个字符。 - site_url: - label: 网站网址 - text: 此网站的网址。 - msg: - empty: 网址不能为空。 - incorrect: 网址格式不正确。 - max_length: 网址长度不得超过 512 个字符。 - contact_email: - label: 联系邮箱 - text: 负责本网站的主要联系人的电子邮件地址。 - msg: - empty: 联系人邮箱不能为空。 - incorrect: 联系人邮箱地址不正确。 - login_required: - label: 私有的 - switch: 需要登录 - text: 只有登录用户才能访问这个社区。 - admin_name: - label: 名字 - msg: 名字不能为空。 - character: '只能由 "a-z", "0-9", " - . _" 组成' - msg_max_length: 名字长度不能超过 30 个字符。 - admin_password: - label: 密码 - text: >- - 您需要此密码才能登录。请将其存储在一个安全的位置。 - msg: 密码不能为空。 - msg_min_length: 密码必须至少 8 个字符长。 - msg_max_length: 密码长度不能超过 32 个字符。 - admin_email: - label: 邮箱 - text: 您需要此电子邮件才能登录。 - msg: - empty: 邮箱不能为空。 - incorrect: 邮箱格式不正确。 - ready_title: 您的网站已准备好 - ready_desc: >- - 如果你想改变更多的设置,请访问 <1>管理区域;在网站菜单中找到它。 - good_luck: "玩得愉快,祝你好运!" - warn_title: 警告 - warn_desc: >- - 文件 <1>config.yaml 已存在。如果你要重置该文件中的任何配置项,请先删除它。 - install_now: 您可以尝试 <1>现在安装。 - installed: 已安裝 - installed_desc: >- - 你似乎已经安装过了。如果要重新安装,请先清除旧的数据库表。 - db_failed: 数据连接异常! - db_failed_desc: >- - 这或者意味着数据库信息在 <1>config.yaml 文件不正确,或者无法与数据库服务器建立联系。这可能意味着你的主机数据库服务器故障。 - counts: - views: 次浏览 - votes: 个点赞 - answers: 个回答 - accepted: 已被采纳 - page_error: - http_error: HTTP 错误 {{ code }} - desc_403: 您无权访问此页面。 - desc_404: 很抱歉,此页面不存在。 - desc_50X: 服务器遇到了一个错误,无法完成你的请求。 - back_home: 返回首页 - page_maintenance: - desc: "我们正在进行维护,我们将很快回来。" - nav_menus: - dashboard: 后台管理 - contents: 内容管理 - questions: 问题 - answers: 回答 - users: 用户管理 - flags: 举报管理 - settings: 站点设置 - general: 一般 - interface: 界面 - smtp: SMTP - branding: 品牌 - legal: 法律条款 - write: 撰写 - tos: 服务条款 - privacy: 隐私政策 - seo: SEO - customize: 自定义 - themes: 主题 - css_html: CSS/HTML - login: 登录 - privileges: 特权 - plugins: 插件 - installed_plugins: 已安装插件 - website_welcome: 欢迎来到 {{site_name}} - user_center: - login: 登录 - qrcode_login_tip: 请使用 {{ agentName }} 扫描二维码并登录。 - login_failed_email_tip: 登录失败,请允许此应用访问您的邮箱信息,然后重试。 - admin: - admin_header: - title: 后台管理 - dashboard: - title: 后台管理 - welcome: 欢迎来到管理后台! - site_statistics: 站点统计 - questions: "问题:" - answers: "回答:" - comments: "评论:" - votes: "投票:" - users: "用户:" - flags: "举报:" - reviews: "审查:" - site_health: 网站健康 - version: "版本" - https: "HTTPS:" - upload_folder: "上传文件夹:" - run_mode: "运行模式:" - private: 私有 - public: 公开 - smtp: "SMTP:" - timezone: "时区:" - system_info: 系统信息 - go_version: "Go版本:" - database: "数据库:" - database_size: "数据库大小:" - storage_used: "已用存储空间:" - uptime: "运行时间:" - links: 链接 - plugins: 插件 - github: GitHub - blog: 博客 - contact: 联系 - forum: 论坛 - documents: 文档 - feedback: 用户反馈 - support: 帮助 - review: 审查 - config: 配置 - update_to: 更新到 - latest: 最新版本 - check_failed: 校验失败 - "yes": "是" - "no": "否" - not_allowed: 拒绝 - allowed: 允许 - enabled: 已启用 - disabled: 停用 - writable: 可写 - not_writable: 不可写 - flags: - title: 举报 - pending: 等待处理 - completed: 已完成 - flagged: 被举报内容 - flagged_type: 标记了 {{ type }} - created: 创建于 - action: 操作 - review: 审查 - user_role_modal: - title: 更改用户状态为... - btn_cancel: 取消 - btn_submit: 提交 - new_password_modal: - title: 设置新密码 - form: - fields: - password: - label: 密码 - text: 用户将被退出,需要再次登录。 - msg: 密码的长度必须是8-32个字符。 - btn_cancel: 取消 - btn_submit: 提交 - edit_profile_modal: - title: 编辑资料 - form: - fields: - username: - label: 用户名 - msg_range: 用户名不能超过 30 个字符。 - email: - label: 电子邮件地址 - msg_invalid: 无效的邮箱地址 - edit_success: 修改成功 - btn_cancel: 取消 - btn_submit: 提交 - user_modal: - title: 添加新用户 - form: - fields: - users: - label: 批量添加用户 - placeholder: "John Smith, john@example.com, BUSYopr2\nAlice, alice@example.com, fpDntV8q" - text: 用逗号分隔“name, email, password”,每行一个用户。 - msg: "请输入用户的邮箱,每行一个。" - display_name: - label: 显示名称 - msg: 显示名称长度必须为 4-30 个字符 - email: - label: 邮箱 - msg: 邮箱无效。 - password: - label: 密码 - msg: 密码的长度必须是8-32个字符。 - btn_cancel: 取消 - btn_submit: 提交 - users: - title: 用户 - name: 名称 - email: 邮箱 - reputation: 声望 - created_at: 创建时间 - delete_at: 删除时间 - suspend_at: 封禁时间 - status: 状态 - role: 角色 - action: 操作 - change: 更改 - all: 全部 - staff: 工作人员 - more: 更多 - inactive: 不活跃 - suspended: 已封禁 - deleted: 已删除 - normal: 正常 - Moderator: 版主 - Admin: 管理员 - User: 用户 - filter: - placeholder: "按名称筛选,用户:id" - set_new_password: 设置新密码 - edit_profile: 编辑资料 - change_status: 更改状态 - change_role: 更改角色 - show_logs: 显示日志 - add_user: 添加用户 - deactivate_user: - title: 停用用户 - content: 未激活的用户必须重新验证他们的邮箱。 - delete_user: - title: 删除此用户 - content: 确定要删除此用户?此操作无法撤销! - remove: 移除内容 - label: 删除所有问题、 答案、 评论等 - text: 如果你只想删除用户账户,请不要选中此项。 - suspend_user: - title: 挂起此用户 - content: 被封禁的用户将无法登录。 - questions: - page_title: 问题 - unlisted: 已隐藏 - post: 标题 - votes: 得票数 - answers: 回答数 - created: 创建于 - status: 状态 - action: 操作 - change: 更改 - pending: 等待处理 - filter: - placeholder: "按标题过滤,问题:id" - answers: - page_title: 回答 - post: 标题 - votes: 得票数 - created: 创建于 - status: 状态 - action: 操作 - change: 更改 - filter: - placeholder: "按标题筛选,答案:id" - general: - page_title: 一般 - name: - label: 站点名称 - msg: 不能为空 - text: "站点的名称,作为站点的标题。" - site_url: - label: 网站网址 - msg: 网站网址不能为空。 - validate: 请输入一个有效的 URL。 - text: 此网站的地址。 - short_desc: - label: 简短站点描述 - msg: 简短网站描述不能为空。 - text: "简短的标语,作为网站主页的标题(Html 的 title 标签)。" - desc: - label: 站点描述 - msg: 网站描述不能为空。 - text: "使用一句话描述本站,作为网站的描述(Html 的 meta 标签)。" - contact_email: - label: 联系邮箱 - msg: 联系人邮箱不能为空。 - validate: 联系人邮箱无效。 - text: 本网站的主要联系邮箱地址。 - check_update: - label: 软件更新 - text: 自动检查软件更新 - interface: - page_title: 界面 - language: - label: 界面语言 - msg: 不能为空 - text: 设置用户界面语言,在刷新页面后生效。 - time_zone: - label: 时区 - msg: 时区不能为空。 - text: 选择一个与您相同时区的城市。 - smtp: - page_title: SMTP - from_email: - label: 发件人邮箱 - msg: 发件人邮箱不能为空。 - text: 用于发送邮件的地址。 - from_name: - label: 发件人 - msg: 不能为空 - text: 发件人的名字。 - smtp_host: - label: SMTP 主机 - msg: 不能为空 - text: 邮件服务器 - encryption: - label: 加密 - msg: 不能为空 - text: 对于大多数服务器而言,SSL 是推荐开启的。 - ssl: SSL - tls: TLS - none: 无加密 - smtp_port: - label: SMTP 端口 - msg: SMTP 端口必须在 1 ~ 65535 之间。 - text: 邮件服务器的端口号。 - smtp_username: - label: SMTP 用户名 - msg: 不能为空 - smtp_password: - label: SMTP 密码 - msg: 不能为空 - test_email_recipient: - label: 测试收件邮箱 - text: 提供用于接收测试邮件的邮箱地址。 - msg: 测试收件邮箱无效 - smtp_authentication: - label: 启用身份验证 - title: SMTP 身份验证 - msg: 不能为空 - "yes": "是" - "no": "否" - branding: - page_title: 品牌 - logo: - label: 网站标志(Logo) - msg: 图标不能为空。 - text: 在你的网站左上方的Logo图标。使用一个高度为56,长宽比大于3:1的宽长方形图像。如果留空,将显示网站标题文本。 - mobile_logo: - label: 移动端 Logo - text: 在你的网站的移动版上使用的标志。使用一个高度为56的宽矩形图像。如果留空,将使用 "Logo"设置中的图像。 - square_icon: - label: 方形图标 - msg: 方形图标不能为空。 - text: 用作元数据图标的基础的图像。最好是大于512x512。 - favicon: - label: 收藏夹图标 - text: 网站的图标。要在 CDN 正常工作,它必须是 png。 将调整大小到32x32。如果留空,将使用“方形图标”。 - legal: - page_title: 法律条款 - terms_of_service: - label: 服务条款 - text: "您可以在此添加服务内容的条款。如果您已经在别处托管了文档,请在这里提供完整的URL。" - privacy_policy: - label: 隐私政策 - text: "您可以在此添加隐私政策内容。如果您已经在别处托管了文档,请在这里提供完整的URL。" - write: - page_title: 编辑 - restrict_answer: - title: 限制一个回答 - label: 每个用户对于每个问题只能有一个回答 - text: "用户可以使用编辑按钮优化已有的回答" - recommend_tags: - label: 推荐标签 - text: "请在上方输入标签固定链接,每行一个标签。" - required_tag: - title: 必需的标签 - label: 根据需要设置推荐标签 - text: "每个新问题必须至少有一个推荐标签。" - reserved_tags: - label: 保留标签 - text: "保留的标签只能由版主添加到一个帖子中。" - seo: - page_title: 搜索引擎优化 - permalink: - label: 固定链接 - text: 自定义URL结构可以提高可用性,以及你的链接的向前兼容性。 - robots: - label: robots.txt - text: 这将永久覆盖任何相关的网站设置。 - themes: - page_title: 主题 - themes: - label: 主题 - text: 选择一个现有主题。 - color_scheme: - label: 配色方案 - navbar_style: - label: 导航栏样式 - primary_color: - label: 主色调 - text: 修改您主题使用的颜色 - css_and_html: - page_title: CSS 与 HTML - custom_css: - label: 自定义 CSS - text: > - - head: - label: 头部 - text: > - - header: - label: 页眉 - text: > - - footer: - label: 页脚 - text: 这将在 之前插入. - sidebar: - label: 侧边栏 - text: 这将插入侧边栏中。 - login: - page_title: 登录 - membership: - title: 会员 - label: 允许新注册 - text: 关闭以防止任何人创建新账户。 - email_registration: - title: 邮箱注册 - label: 允许邮箱注册 - text: 关闭以阻止任何人通过邮箱创建新账户。 - allowed_email_domains: - title: 允许的邮箱域 - text: 允许注册账户的邮箱域。每行一个域名。留空时忽略。 - private: - title: 非公开的 - label: 需要登录 - text: 只有登录用户才能访问这个社区。 - password_login: - title: 密码登录 - label: 允许使用邮箱和密码登录 - text: "警告:如果您未配置过其他登录方式,关闭密码登录后您则可能无法登录。" - installed_plugins: - title: 已安装插件 - plugin_link: 插件扩展功能。您可以在<1>插件仓库中找到插件。 - filter: - all: 全部 - active: 已启用 - inactive: 未启用 - outdated: 已过期 - plugins: - label: 插件 - text: 选择一个现有的插件。 - name: 名称 - version: 版本 - status: 状态 - action: 操作 - deactivate: 停用 - activate: 启用 - settings: 设置 - settings_users: - title: 用户 - avatar: - label: 默认头像 - text: 没有自定义头像的用户。 - gravatar_base_url: - label: Gravatar 根路径 URL - text: Gravatar 提供商的 API 基础的 URL。当为空时忽略。 - profile_editable: - title: 个人资料可编辑 - allow_update_display_name: - label: 允许用户修改显示名称 - allow_update_username: - label: 允许用户修改用户名 - allow_update_avatar: - label: 允许用户修改个人头像 - allow_update_bio: - label: 允许用户修改个人介绍 - allow_update_website: - label: 允许用户修改个人主页网址 - allow_update_location: - label: 允许用户更改位置 - privilege: - title: 特权 - level: - label: 级别所需声望 - text: 选择特权所需的声望值 - msg: - should_be_number: 输入必须是数字 - number_larger_1: 数字应该大于等于 1 - form: - optional: (选填) - empty: 不能为空 - invalid: 是无效的 - btn_submit: 保存 - not_found_props: "所需属性 {{ key }} 未找到。" - select: 选择 - page_review: - review: 评论 - proposed: 提案 - question_edit: 问题编辑 - answer_edit: 回答编辑 - tag_edit: '标签管理: 编辑标签' - edit_summary: 编辑备注 - edit_question: 编辑问题 - edit_answer: 编辑回答 - edit_tag: 编辑标签 - empty: 没有剩余的审核任务。 - approve_revision_tip: 您是否批准此修订? - approve_flag_tip: 您是否批准此举报? - approve_post_tip: 您是否批准此帖子? - approve_user_tip: 您是否批准此修订? - suggest_edits: 建议的编辑 - flag_post: 举报帖子 - flag_user: 举报用户 - queued_post: 排队的帖子 - queued_user: 排队用户 - filter_label: 类型 - reputation: 声望值 - flag_post_type: 举报这个帖子的类型是 {{ type }} - flag_user_type: 举报这个用户的类型是 {{ type }} - edit_post: 编辑帖子 - list_post: 文章列表 - unlist_post: 隐藏的帖子 - timeline: - undeleted: 取消删除 - deleted: 删除 - downvote: 反对 - upvote: 点赞 - accept: 采纳 - cancelled: 已取消 - commented: '评论:' - rollback: 回滚 - edited: 最后编辑于 - answered: 回答于 - asked: 提问于 - closed: 关闭 - reopened: 重新开启 - created: 创建于 - pin: 已置顶 - unpin: 取消置頂 - show: 已显示 - hide: 已隐藏 - title: "历史记录" - tag_title: "时间线" - show_votes: "显示投票" - n_or_a: N/A - title_for_question: "时间线" - title_for_answer: "{{ title }} 的 {{ author }} 回答时间线" - title_for_tag: "时间线" - datetime: 日期时间 - type: 类型 - by: 由 - comment: 评论 - no_data: "空空如也" - users: - title: 用户 - users_with_the_most_reputation: 本周声望最高的用户 - users_with_the_most_vote: 本周投票最多的用户 - staffs: 我们的社区工作人员 - reputation: 声望值 - votes: 投票 - prompt: - leave_page: 确定要离开此页面? - changes_not_save: 您的更改尚未保存 - draft: - discard_confirm: 您确定要丢弃您的草稿吗? - messages: - post_deleted: 该帖子已被删除。 - post_pin: 该帖子已被置顶。 - post_unpin: 该帖子已被取消置顶。 - post_hide_list: 此帖子已经从列表中隐藏。 - post_show_list: 该帖子已显示到列表中。 - post_reopen: 这个帖子已被重新打开. - post_list: 这个帖子已经被显示 - post_unlist: 这个帖子已经被隐藏 - post_pending: 您的帖子正在等待审核。它将在它获得批准后可见。 From 1d9f88f803cb6427c9eb7d1166436494a2314d83 Mon Sep 17 00:00:00 2001 From: kumfo Date: Mon, 19 Aug 2024 16:15:21 +0800 Subject: [PATCH 18/64] fix(embed): embed plugin type definition --- docs/docs.go | 90 ++++++++++++++++++------ docs/swagger.json | 92 ++++++++++++++++++------- docs/swagger.yaml | 65 ++++++++++++----- internal/controller/embed_controller.go | 22 ++---- internal/schema/plugin_option_schema.go | 25 ------- plugin/embed.go | 8 +++ 6 files changed, 195 insertions(+), 107 deletions(-) delete mode 100644 internal/schema/plugin_option_schema.go diff --git a/docs/docs.go b/docs/docs.go index cf1264d14..fab0d8e9b 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -2738,7 +2738,7 @@ const docTemplate = `{ "data": { "type": "array", "items": { - "$ref": "#/definitions/schema.GetEmbedOptionResp" + "$ref": "#/definitions/plugin.EmbedConfig" } } } @@ -4410,7 +4410,7 @@ const docTemplate = `{ "data": { "type": "array", "items": { - "$ref": "#/definitions/schema.GetTagResp" + "$ref": "#/definitions/schema.GetTagBasicResp" } } } @@ -5380,7 +5380,7 @@ const docTemplate = `{ }, "/answer/api/v1/tags": { "get": { - "description": "get tags list", + "description": "get tags list by slug name", "produces": [ "application/json" ], @@ -5404,7 +5404,22 @@ const docTemplate = `{ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/handler.RespBody" + "allOf": [ + { + "$ref": "#/definitions/handler.RespBody" + }, + { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/schema.GetTagBasicResp" + } + } + } + } + ] } } } @@ -6571,14 +6586,14 @@ const docTemplate = `{ }, "/custom.css": { "get": { - "description": "get site robots information", + "description": "get site custom CSS", "produces": [ - "application/json" + "text/css" ], "tags": [ "site" ], - "summary": "get site robots information", + "summary": "get site custom CSS", "responses": { "200": { "description": "OK", @@ -7004,6 +7019,17 @@ const docTemplate = `{ "list": {} } }, + "plugin.EmbedConfig": { + "type": "object", + "properties": { + "enable": { + "type": "boolean" + }, + "platform": { + "type": "string" + } + } + }, "schema.AcceptAnswerReq": { "type": "object", "required": [ @@ -7813,17 +7839,6 @@ const docTemplate = `{ } } }, - "schema.GetEmbedOptionResp": { - "type": "object", - "properties": { - "enable": { - "type": "boolean" - }, - "platform": { - "type": "string" - } - } - }, "schema.GetFollowingTagsResp": { "type": "object", "properties": { @@ -8242,6 +8257,23 @@ const docTemplate = `{ } } }, + "schema.GetTagBasicResp": { + "type": "object", + "properties": { + "display_name": { + "type": "string" + }, + "recommend": { + "type": "boolean" + }, + "reserved": { + "type": "boolean" + }, + "slug_name": { + "type": "string" + } + } + }, "schema.GetTagPageResp": { "type": "object", "properties": { @@ -9819,7 +9851,7 @@ const docTemplate = `{ "recommend_tags": { "type": "array", "items": { - "type": "string" + "$ref": "#/definitions/schema.SiteWriteTag" } }, "required_tag": { @@ -9828,7 +9860,7 @@ const docTemplate = `{ "reserved_tags": { "type": "array", "items": { - "type": "string" + "$ref": "#/definitions/schema.SiteWriteTag" } }, "restrict_answer": { @@ -9842,7 +9874,7 @@ const docTemplate = `{ "recommend_tags": { "type": "array", "items": { - "type": "string" + "$ref": "#/definitions/schema.SiteWriteTag" } }, "required_tag": { @@ -9851,7 +9883,7 @@ const docTemplate = `{ "reserved_tags": { "type": "array", "items": { - "type": "string" + "$ref": "#/definitions/schema.SiteWriteTag" } }, "restrict_answer": { @@ -9859,6 +9891,20 @@ const docTemplate = `{ } } }, + "schema.SiteWriteTag": { + "type": "object", + "required": [ + "slug_name" + ], + "properties": { + "display_name": { + "type": "string" + }, + "slug_name": { + "type": "string" + } + } + }, "schema.TagItem": { "type": "object", "properties": { diff --git a/docs/swagger.json b/docs/swagger.json index 1e93d2b08..9ea2d2ee4 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -2708,7 +2708,7 @@ "data": { "type": "array", "items": { - "$ref": "#/definitions/schema.GetEmbedOptionResp" + "$ref": "#/definitions/plugin.EmbedConfig" } } } @@ -4380,7 +4380,7 @@ "data": { "type": "array", "items": { - "$ref": "#/definitions/schema.GetTagResp" + "$ref": "#/definitions/schema.GetTagBasicResp" } } } @@ -5350,7 +5350,7 @@ }, "/answer/api/v1/tags": { "get": { - "description": "get tags list", + "description": "get tags list by slug name", "produces": [ "application/json" ], @@ -5374,7 +5374,22 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/handler.RespBody" + "allOf": [ + { + "$ref": "#/definitions/handler.RespBody" + }, + { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/schema.GetTagBasicResp" + } + } + } + } + ] } } } @@ -6541,14 +6556,14 @@ }, "/custom.css": { "get": { - "description": "get site robots information", + "description": "get site custom CSS", "produces": [ - "application/json" + "text/css" ], "tags": [ "site" ], - "summary": "get site robots information", + "summary": "get site custom CSS", "responses": { "200": { "description": "OK", @@ -6974,6 +6989,17 @@ "list": {} } }, + "plugin.EmbedConfig": { + "type": "object", + "properties": { + "enable": { + "type": "boolean" + }, + "platform": { + "type": "string" + } + } + }, "schema.AcceptAnswerReq": { "type": "object", "required": [ @@ -7783,17 +7809,6 @@ } } }, - "schema.GetEmbedOptionResp": { - "type": "object", - "properties": { - "enable": { - "type": "boolean" - }, - "platform": { - "type": "string" - } - } - }, "schema.GetFollowingTagsResp": { "type": "object", "properties": { @@ -8212,6 +8227,23 @@ } } }, + "schema.GetTagBasicResp": { + "type": "object", + "properties": { + "display_name": { + "type": "string" + }, + "recommend": { + "type": "boolean" + }, + "reserved": { + "type": "boolean" + }, + "slug_name": { + "type": "string" + } + } + }, "schema.GetTagPageResp": { "type": "object", "properties": { @@ -8809,7 +8841,7 @@ "enum": [ "newest", "active", - "frequent", + "hot", "score", "unanswered" ] @@ -9789,7 +9821,7 @@ "recommend_tags": { "type": "array", "items": { - "type": "string" + "$ref": "#/definitions/schema.SiteWriteTag" } }, "required_tag": { @@ -9798,7 +9830,7 @@ "reserved_tags": { "type": "array", "items": { - "type": "string" + "$ref": "#/definitions/schema.SiteWriteTag" } }, "restrict_answer": { @@ -9812,7 +9844,7 @@ "recommend_tags": { "type": "array", "items": { - "type": "string" + "$ref": "#/definitions/schema.SiteWriteTag" } }, "required_tag": { @@ -9821,7 +9853,7 @@ "reserved_tags": { "type": "array", "items": { - "type": "string" + "$ref": "#/definitions/schema.SiteWriteTag" } }, "restrict_answer": { @@ -9829,6 +9861,20 @@ } } }, + "schema.SiteWriteTag": { + "type": "object", + "required": [ + "slug_name" + ], + "properties": { + "display_name": { + "type": "string" + }, + "slug_name": { + "type": "string" + } + } + }, "schema.TagItem": { "type": "object", "properties": { diff --git a/docs/swagger.yaml b/docs/swagger.yaml index 93f8116af..1b176960d 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -117,6 +117,13 @@ definitions: type: integer list: {} type: object + plugin.EmbedConfig: + properties: + enable: + type: boolean + platform: + type: string + type: object schema.AcceptAnswerReq: properties: answer_id: @@ -687,13 +694,6 @@ definitions: description: website type: string type: object - schema.GetEmbedOptionResp: - properties: - enable: - type: boolean - platform: - type: string - type: object schema.GetFollowingTagsResp: properties: display_name: @@ -983,6 +983,17 @@ definitions: terms_of_service_parsed_text: type: string type: object + schema.GetTagBasicResp: + properties: + display_name: + type: string + recommend: + type: boolean + reserved: + type: boolean + slug_name: + type: string + type: object schema.GetTagPageResp: properties: created_at: @@ -1401,7 +1412,7 @@ definitions: enum: - newest - active - - frequent + - hot - score - unanswered type: string @@ -2071,13 +2082,13 @@ definitions: properties: recommend_tags: items: - type: string + $ref: '#/definitions/schema.SiteWriteTag' type: array required_tag: type: boolean reserved_tags: items: - type: string + $ref: '#/definitions/schema.SiteWriteTag' type: array restrict_answer: type: boolean @@ -2086,17 +2097,26 @@ definitions: properties: recommend_tags: items: - type: string + $ref: '#/definitions/schema.SiteWriteTag' type: array required_tag: type: boolean reserved_tags: items: - type: string + $ref: '#/definitions/schema.SiteWriteTag' type: array restrict_answer: type: boolean type: object + schema.SiteWriteTag: + properties: + display_name: + type: string + slug_name: + type: string + required: + - slug_name + type: object schema.TagItem: properties: display_name: @@ -4353,7 +4373,7 @@ paths: - properties: data: items: - $ref: '#/definitions/schema.GetEmbedOptionResp' + $ref: '#/definitions/plugin.EmbedConfig' type: array type: object summary: GetEmbedConfig @@ -5382,7 +5402,7 @@ paths: - properties: data: items: - $ref: '#/definitions/schema.GetTagResp' + $ref: '#/definitions/schema.GetTagBasicResp' type: array type: object security: @@ -5965,7 +5985,7 @@ paths: - Tag /answer/api/v1/tags: get: - description: get tags list + description: get tags list by slug name parameters: - collectionFormat: csv description: string collection @@ -5980,7 +6000,14 @@ paths: "200": description: OK schema: - $ref: '#/definitions/handler.RespBody' + allOf: + - $ref: '#/definitions/handler.RespBody' + - properties: + data: + items: + $ref: '#/definitions/schema.GetTagBasicResp' + type: array + type: object summary: get tags list tags: - Tag @@ -6677,15 +6704,15 @@ paths: - Activity /custom.css: get: - description: get site robots information + description: get site custom CSS produces: - - application/json + - text/css responses: "200": description: OK schema: type: string - summary: get site robots information + summary: get site custom CSS tags: - site /installation/base-info: diff --git a/internal/controller/embed_controller.go b/internal/controller/embed_controller.go index c9691d299..a61bd68cf 100644 --- a/internal/controller/embed_controller.go +++ b/internal/controller/embed_controller.go @@ -21,7 +21,6 @@ package controller import ( "github.com/apache/incubator-answer/internal/base/handler" - "github.com/apache/incubator-answer/internal/schema" "github.com/apache/incubator-answer/plugin" "github.com/gin-gonic/gin" ) @@ -40,27 +39,14 @@ func NewEmbedController() *EmbedController { // @Accept json // @Produce json // @Router /answer/api/v1/embed/config [get] -// @Success 200 {object} handler.RespBody{data=[]schema.GetEmbedOptionResp} +// @Success 200 {object} handler.RespBody{data=[]plugin.EmbedConfig} func (c *EmbedController) GetEmbedConfig(ctx *gin.Context) { - resp := make([]*schema.GetEmbedOptionResp, 0) - var slugName string + resp := make([]*plugin.EmbedConfig, 0) - _ = plugin.CallEmbed(func(base plugin.Embed) error { - slugName = base.Info().SlugName + _ = plugin.CallEmbed(func(embed plugin.Embed) (err error) { + resp, err = embed.GetEmbedConfigs(ctx) return nil }) - _ = plugin.CallConfig(func(fn plugin.Config) error { - if fn.Info().SlugName == slugName { - for _, field := range fn.ConfigFields() { - resp = append(resp, &schema.GetEmbedOptionResp{ - Platform: field.Name, - Enable: field.Value.(bool), - }) - } - return nil - } - return nil - }) handler.HandleResponse(ctx, nil, resp) } diff --git a/internal/schema/plugin_option_schema.go b/internal/schema/plugin_option_schema.go deleted file mode 100644 index 8db229738..000000000 --- a/internal/schema/plugin_option_schema.go +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package schema - -type GetEmbedOptionResp struct { - Platform string `json:"platform"` - Enable bool `json:"enable"` -} diff --git a/plugin/embed.go b/plugin/embed.go index 55149f664..e853c8c27 100644 --- a/plugin/embed.go +++ b/plugin/embed.go @@ -19,8 +19,16 @@ package plugin +import "github.com/gin-gonic/gin" + +type EmbedConfig struct { + Platform string `json:"platform"` + Enable bool `json:"enable"` +} + type Embed interface { Base + GetEmbedConfigs(ctx *gin.Context) (embedConfigs []*EmbedConfig, err error) } var ( From 3d8cf0ef418f57524d4b1403c91453059e307fb7 Mon Sep 17 00:00:00 2001 From: shuai Date: Mon, 5 Aug 2024 15:00:54 +0800 Subject: [PATCH 19/64] feat: bages ui --- i18n/en_US.yaml | 10 + ui/package.json | 5 +- ui/pnpm-lock.yaml | 1020 ++++++++++++++++- ui/src/components/CardBadge/index.scss | 36 + ui/src/components/CardBadge/index.tsx | 51 + ui/src/components/SideNav/index.tsx | 5 + ui/src/components/index.ts | 2 + ui/src/pages/Badges/Detail/index.tsx | 97 ++ ui/src/pages/Badges/index.tsx | 45 + .../Users/Personal/components/Alert/index.tsx | 23 +- .../Personal/components/Badges/index.tsx | 43 + .../Personal/components/NavBar/index.tsx | 5 + .../Personal/components/Overview/index.tsx | 36 +- .../Personal/components/TopList/index.tsx | 56 +- .../pages/Users/Personal/components/index.ts | 2 + ui/src/pages/Users/Personal/index.tsx | 70 +- ui/src/router/routes.ts | 8 + 17 files changed, 1393 insertions(+), 121 deletions(-) create mode 100644 ui/src/components/CardBadge/index.scss create mode 100644 ui/src/components/CardBadge/index.tsx create mode 100644 ui/src/pages/Badges/Detail/index.tsx create mode 100644 ui/src/pages/Badges/index.tsx create mode 100644 ui/src/pages/Users/Personal/components/Badges/index.tsx diff --git a/i18n/en_US.yaml b/i18n/en_US.yaml index 4de696589..f9787eda3 100644 --- a/i18n/en_US.yaml +++ b/i18n/en_US.yaml @@ -1155,6 +1155,7 @@ ui: question: Questions tag: Tags user: Users + badges: Badges profile: Profile setting: Settings logout: Log out @@ -1572,12 +1573,14 @@ ui: reputation: Reputation comments: Comments votes: Votes + badges: Badges newest: Newest score: Score edit_profile: Edit profile visited_x_days: "Visited {{ count }} days" viewed: Viewed joined: Joined + comma: ',' last_login: Seen about_me: About Me about_me_empty: "// Hello, World !" @@ -1733,6 +1736,13 @@ ui: login: Login qrcode_login_tip: Please use {{ agentName }} to scan the QR code and log in. login_failed_email_tip: Login failed, please allow this app to access your email information before try again. + badges: + title: Badges + awarded: Awarded + earned_x: Earned x{{ number }} + x_awarded: "{{ number }} awarded" + can_earn_multiple: You can earn this multiple times. + admin: admin_header: diff --git a/ui/package.json b/ui/package.json index 8a087428c..0ae727e23 100644 --- a/ui/package.json +++ b/ui/package.json @@ -45,7 +45,8 @@ "react-router-dom": "^6.22.3", "semver": "^7.3.8", "swr": "^1.3.0", - "zustand": "^4.1.1" + "zustand": "^4.1.1", + "demo": "workspace:*" }, "devDependencies": { "@commitlint/cli": "^17.0.3", @@ -98,4 +99,4 @@ "pnpm": ">=8" }, "license": "MIT" -} +} \ No newline at end of file diff --git a/ui/pnpm-lock.yaml b/ui/pnpm-lock.yaml index 77bbee2d4..50fdae8f5 100644 --- a/ui/pnpm-lock.yaml +++ b/ui/pnpm-lock.yaml @@ -44,6 +44,9 @@ importers: dayjs: specifier: ^1.11.5 version: 1.11.5 + demo: + specifier: workspace:* + version: link:src/plugins/demo diff: specifier: ^5.1.0 version: 5.1.0 @@ -229,6 +232,52 @@ importers: specifier: ^0.8.0 version: 0.8.0 + src/plugins/demo: + dependencies: + react: + specifier: ^18.2.0 + version: 18.2.0 + react-bootstrap: + specifier: ^2.10.0 + version: 2.10.0(@types/react@18.0.20)(react-dom@18.2.0)(react@18.2.0) + react-dom: + specifier: ^18.2.0 + version: 18.2.0(react@18.2.0) + react-i18next: + specifier: ^11.18.3 + version: 11.18.6(i18next@21.9.2)(react-dom@18.2.0)(react@18.2.0) + devDependencies: + '@modyfi/vite-plugin-yaml': + specifier: ^1.1.0 + version: 1.1.0(rollup@2.79.0)(vite@4.5.3) + '@typescript-eslint/eslint-plugin': + specifier: ^6.0.0 + version: 6.11.0(@typescript-eslint/parser@6.11.0)(eslint@8.53.0)(typescript@5.5.4) + '@typescript-eslint/parser': + specifier: ^6.0.0 + version: 6.11.0(eslint@8.53.0)(typescript@5.5.4) + '@vitejs/plugin-react-swc': + specifier: ^3.3.2 + version: 3.7.0(vite@4.5.3) + eslint: + specifier: ^8.45.0 + version: 8.53.0 + eslint-plugin-react-hooks: + specifier: ^4.6.0 + version: 4.6.0(eslint@8.53.0) + eslint-plugin-react-refresh: + specifier: ^0.4.3 + version: 0.4.9(eslint@8.53.0) + typescript: + specifier: ^5.0.2 + version: 5.5.4 + vite: + specifier: ^4.4.5 + version: 4.5.3(@types/node@16.11.59)(sass@1.54.9) + vite-plugin-dts: + specifier: ^3.9.1 + version: 3.9.1(@types/node@16.11.59)(rollup@2.79.0)(typescript@5.5.4)(vite@4.5.3) + packages: /@aashutoshrathi/word-wrap@1.2.6: @@ -549,6 +598,14 @@ packages: dependencies: '@babel/types': 7.19.0 + /@babel/parser@7.25.0: + resolution: {integrity: sha512-CzdIU9jdP0dg7HdyB+bHvDJGagUv+qtzZt5rYCWwW6tITNqV9odjp6Qu41gkG0ca5UfdDUWrKkiAnHHdGRnOrA==} + engines: {node: '>=6.0.0'} + hasBin: true + dependencies: + '@babel/types': 7.23.6 + dev: true + /@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.18.6(@babel/core@7.19.1): resolution: {integrity: sha512-Dgxsyg54Fx1d4Nge8UnvTrED63vrwOdPmyvPzlNN/boaliRP54pm3pGzZD1SJUwrBA+Cs/xdG8kXX6Mn/RfISQ==} engines: {node: '>=6.9.0'} @@ -2191,6 +2248,204 @@ packages: postcss: 8.4.16 postcss-selector-parser: 6.0.10 + /@esbuild/android-arm64@0.18.20: + resolution: {integrity: sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/android-arm@0.18.20: + resolution: {integrity: sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/android-x64@0.18.20: + resolution: {integrity: sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/darwin-arm64@0.18.20: + resolution: {integrity: sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@esbuild/darwin-x64@0.18.20: + resolution: {integrity: sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@esbuild/freebsd-arm64@0.18.20: + resolution: {integrity: sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/freebsd-x64@0.18.20: + resolution: {integrity: sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-arm64@0.18.20: + resolution: {integrity: sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-arm@0.18.20: + resolution: {integrity: sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-ia32@0.18.20: + resolution: {integrity: sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-loong64@0.18.20: + resolution: {integrity: sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-mips64el@0.18.20: + resolution: {integrity: sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-ppc64@0.18.20: + resolution: {integrity: sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-riscv64@0.18.20: + resolution: {integrity: sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-s390x@0.18.20: + resolution: {integrity: sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-x64@0.18.20: + resolution: {integrity: sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/netbsd-x64@0.18.20: + resolution: {integrity: sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/openbsd-x64@0.18.20: + resolution: {integrity: sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/sunos-x64@0.18.20: + resolution: {integrity: sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + requiresBuild: true + dev: true + optional: true + + /@esbuild/win32-arm64@0.18.20: + resolution: {integrity: sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@esbuild/win32-ia32@0.18.20: + resolution: {integrity: sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@esbuild/win32-x64@0.18.20: + resolution: {integrity: sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: true + optional: true + /@eslint-community/eslint-utils@4.4.0(eslint@8.53.0): resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -2495,14 +2750,14 @@ packages: engines: {node: '>=6.0.0'} dependencies: '@jridgewell/set-array': 1.1.2 - '@jridgewell/sourcemap-codec': 1.4.14 + '@jridgewell/sourcemap-codec': 1.4.15 /@jridgewell/gen-mapping@0.3.2: resolution: {integrity: sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==} engines: {node: '>=6.0.0'} dependencies: '@jridgewell/set-array': 1.1.2 - '@jridgewell/sourcemap-codec': 1.4.14 + '@jridgewell/sourcemap-codec': 1.4.15 '@jridgewell/trace-mapping': 0.3.17 /@jridgewell/gen-mapping@0.3.3: @@ -2510,7 +2765,7 @@ packages: engines: {node: '>=6.0.0'} dependencies: '@jridgewell/set-array': 1.1.2 - '@jridgewell/sourcemap-codec': 1.4.15 + '@jridgewell/sourcemap-codec': 1.5.0 '@jridgewell/trace-mapping': 0.3.22 dev: true @@ -2545,6 +2800,9 @@ packages: /@jridgewell/sourcemap-codec@1.4.15: resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} + + /@jridgewell/sourcemap-codec@1.5.0: + resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} dev: true /@jridgewell/trace-mapping@0.3.15: @@ -2563,14 +2821,14 @@ packages: resolution: {integrity: sha512-Wf963MzWtA2sjrNt+g18IAln9lKnlRp+K2eH4jjIoF1wYeq3aMREpG09xhlhdzS0EjwU7qmUJYangWa+151vZw==} dependencies: '@jridgewell/resolve-uri': 3.1.1 - '@jridgewell/sourcemap-codec': 1.4.15 + '@jridgewell/sourcemap-codec': 1.5.0 dev: true /@jridgewell/trace-mapping@0.3.9: resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} dependencies: '@jridgewell/resolve-uri': 3.1.0 - '@jridgewell/sourcemap-codec': 1.4.14 + '@jridgewell/sourcemap-codec': 1.4.15 /@leichtgewicht/ip-codec@2.0.4: resolution: {integrity: sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==} @@ -2702,6 +2960,63 @@ packages: '@lezer/lr': 1.4.0 dev: false + /@microsoft/api-extractor-model@7.28.13(@types/node@16.11.59): + resolution: {integrity: sha512-39v/JyldX4MS9uzHcdfmjjfS6cYGAoXV+io8B5a338pkHiSt+gy2eXQ0Q7cGFJ7quSa1VqqlMdlPrB6sLR/cAw==} + dependencies: + '@microsoft/tsdoc': 0.14.2 + '@microsoft/tsdoc-config': 0.16.2 + '@rushstack/node-core-library': 4.0.2(@types/node@16.11.59) + transitivePeerDependencies: + - '@types/node' + dev: true + + /@microsoft/api-extractor@7.43.0(@types/node@16.11.59): + resolution: {integrity: sha512-GFhTcJpB+MI6FhvXEI9b2K0snulNLWHqC/BbcJtyNYcKUiw7l3Lgis5ApsYncJ0leALX7/of4XfmXk+maT111w==} + hasBin: true + dependencies: + '@microsoft/api-extractor-model': 7.28.13(@types/node@16.11.59) + '@microsoft/tsdoc': 0.14.2 + '@microsoft/tsdoc-config': 0.16.2 + '@rushstack/node-core-library': 4.0.2(@types/node@16.11.59) + '@rushstack/rig-package': 0.5.2 + '@rushstack/terminal': 0.10.0(@types/node@16.11.59) + '@rushstack/ts-command-line': 4.19.1(@types/node@16.11.59) + lodash: 4.17.21 + minimatch: 3.0.4 + resolve: 1.22.1 + semver: 7.5.4 + source-map: 0.6.1 + typescript: 5.4.2 + transitivePeerDependencies: + - '@types/node' + dev: true + + /@microsoft/tsdoc-config@0.16.2: + resolution: {integrity: sha512-OGiIzzoBLgWWR0UdRJX98oYO+XKGf7tiK4Zk6tQ/E4IJqGCe7dvkTvgDZV5cFJUzLGDOjeAXrnZoA6QkVySuxw==} + dependencies: + '@microsoft/tsdoc': 0.14.2 + ajv: 6.12.6 + jju: 1.4.0 + resolve: 1.19.0 + dev: true + + /@microsoft/tsdoc@0.14.2: + resolution: {integrity: sha512-9b8mPpKrfeGRuhFH5iO1iwCLeIIsV6+H1sRfxbkoGXIyQE2BTsPd9zqSqQJ+pv5sJ/hT5M1zvOFL02MnEezFug==} + dev: true + + /@modyfi/vite-plugin-yaml@1.1.0(rollup@2.79.0)(vite@4.5.3): + resolution: {integrity: sha512-L26xfzkSo1yamODCAtk/ipVlL6OEw2bcJ92zunyHu8zxi7+meV0zefA9xscRMDCsMY8xL3C3wi3DhMiPxcbxbw==} + peerDependencies: + vite: ^3.2.7 || ^4.0.5 || ^5.0.5 + dependencies: + '@rollup/pluginutils': 5.1.0(rollup@2.79.0) + js-yaml: 4.1.0 + tosource: 2.0.0-alpha.3 + vite: 4.5.3(@types/node@16.11.59)(sass@1.54.9) + transitivePeerDependencies: + - rollup + dev: true + /@nicolo-ribaudo/eslint-scope-5-internals@5.1.1-v1: resolution: {integrity: sha512-54/JRvkLIzzDWshCWfuhadfrfZVPiElY8Fcgmg1HroEly/EDSszzhBAsarCux+D/kOslTRquNzuyGSmUSTTHGg==} dependencies: @@ -2873,9 +3188,72 @@ packages: picomatch: 2.3.1 rollup: 2.79.0 + /@rollup/pluginutils@5.1.0(rollup@2.79.0): + resolution: {integrity: sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + dependencies: + '@types/estree': 1.0.5 + estree-walker: 2.0.2 + picomatch: 2.3.1 + rollup: 2.79.0 + dev: true + /@rushstack/eslint-patch@1.2.0: resolution: {integrity: sha512-sXo/qW2/pAcmT43VoRKOJbDOfV3cYpq3szSVfIThQXNt+E4DfKj361vaAt3c88U5tPUxzEswam7GW48PJqtKAg==} + /@rushstack/node-core-library@4.0.2(@types/node@16.11.59): + resolution: {integrity: sha512-hyES82QVpkfQMeBMteQUnrhASL/KHPhd7iJ8euduwNJG4mu2GSOKybf0rOEjOm1Wz7CwJEUm9y0yD7jg2C1bfg==} + peerDependencies: + '@types/node': '*' + peerDependenciesMeta: + '@types/node': + optional: true + dependencies: + '@types/node': 16.11.59 + fs-extra: 7.0.1 + import-lazy: 4.0.0 + jju: 1.4.0 + resolve: 1.22.1 + semver: 7.5.4 + z-schema: 5.0.5 + dev: true + + /@rushstack/rig-package@0.5.2: + resolution: {integrity: sha512-mUDecIJeH3yYGZs2a48k+pbhM6JYwWlgjs2Ca5f2n1G2/kgdgP9D/07oglEGf6mRyXEnazhEENeYTSNDRCwdqA==} + dependencies: + resolve: 1.22.1 + strip-json-comments: 3.1.1 + dev: true + + /@rushstack/terminal@0.10.0(@types/node@16.11.59): + resolution: {integrity: sha512-UbELbXnUdc7EKwfH2sb8ChqNgapUOdqcCIdQP4NGxBpTZV2sQyeekuK3zmfQSa/MN+/7b4kBogl2wq0vpkpYGw==} + peerDependencies: + '@types/node': '*' + peerDependenciesMeta: + '@types/node': + optional: true + dependencies: + '@rushstack/node-core-library': 4.0.2(@types/node@16.11.59) + '@types/node': 16.11.59 + supports-color: 8.1.1 + dev: true + + /@rushstack/ts-command-line@4.19.1(@types/node@16.11.59): + resolution: {integrity: sha512-J7H768dgcpG60d7skZ5uSSwyCZs/S2HrWP1Ds8d1qYAyaaeJmpmmLr9BVw97RjFzmQPOYnoXcKA4GkqDCkduQg==} + dependencies: + '@rushstack/terminal': 0.10.0(@types/node@16.11.59) + '@types/argparse': 1.0.38 + argparse: 1.0.10 + string-argv: 0.3.2 + transitivePeerDependencies: + - '@types/node' + dev: true + /@sinclair/typebox@0.24.42: resolution: {integrity: sha512-d+2AtrHGyWek2u2ITF0lHRIv6Tt7X0dEHW+0rP+5aDCEjC3fiN2RBjrLD0yU0at52BcZbRGxLbAtXiR0hFCjYw==} @@ -2992,12 +3370,141 @@ packages: transitivePeerDependencies: - supports-color + /@swc/core-darwin-arm64@1.7.3: + resolution: {integrity: sha512-CTkHa6MJdov9t41vuV2kmQIMu+Q19LrEHGIR/UiJYH06SC/sOu35ZZH8DyfLp9ZoaCn21gwgWd61ixOGQlwzTw==} + engines: {node: '>=10'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@swc/core-darwin-x64@1.7.3: + resolution: {integrity: sha512-mun623y6rCoZ2EFIYfIRqXYRFufJOopoYSJcxYhZUrfTpAvQ1zLngjQpWCUU1krggXR2U0PQj+ls0DfXUTraNg==} + engines: {node: '>=10'} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@swc/core-linux-arm-gnueabihf@1.7.3: + resolution: {integrity: sha512-4Jz4UcIcvZNMp9qoHbBx35bo3rjt8hpYLPqnR4FFq6gkAsJIMFC56UhRZwdEQoDuYiOFMBnnrsg31Fyo6YQypA==} + engines: {node: '>=10'} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@swc/core-linux-arm64-gnu@1.7.3: + resolution: {integrity: sha512-p+U/M/oqV7HC4erQ5TVWHhJU1984QD+wQBPxslAYq751bOQGm0R/mXK42GjugqjnR6yYrAiwKKbpq4iWVXNePA==} + engines: {node: '>=10'} + cpu: [arm64] + os: [linux] + libc: [glibc] + requiresBuild: true + dev: true + optional: true + + /@swc/core-linux-arm64-musl@1.7.3: + resolution: {integrity: sha512-s6VzyaJwaRGTi2mz2h6Ywxfmgpkc69IxhuMzl+sl34plH0V0RgnZDm14HoCGIKIzRk4+a2EcBV1ZLAfWmPACQg==} + engines: {node: '>=10'} + cpu: [arm64] + os: [linux] + libc: [musl] + requiresBuild: true + dev: true + optional: true + + /@swc/core-linux-x64-gnu@1.7.3: + resolution: {integrity: sha512-IrFY48C356Z2dU2pjYg080yvMXzmSV3Lmm/Wna4cfcB1nkVLjWsuYwwRAk9CY7E19c+q8N1sMNggubAUDYoX2g==} + engines: {node: '>=10'} + cpu: [x64] + os: [linux] + libc: [glibc] + requiresBuild: true + dev: true + optional: true + + /@swc/core-linux-x64-musl@1.7.3: + resolution: {integrity: sha512-qoLgxBlBnnyUEDu5vmRQqX90h9jldU1JXI96e6eh2d1gJyKRA0oSK7xXmTzorv1fGHiHulv9qiJOUG+g6uzJWg==} + engines: {node: '>=10'} + cpu: [x64] + os: [linux] + libc: [musl] + requiresBuild: true + dev: true + optional: true + + /@swc/core-win32-arm64-msvc@1.7.3: + resolution: {integrity: sha512-OAd7jVVJ7nb0Ev80VAa1aeK+FldPeC4eZ35H4Qn6EICzIz0iqJo2T33qLKkSZiZEBKSoF4KcwrqYfkjLOp5qWg==} + engines: {node: '>=10'} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@swc/core-win32-ia32-msvc@1.7.3: + resolution: {integrity: sha512-31+Le1NyfSnILFV9+AhxfFOG0DK0272MNhbIlbcv4w/iqpjkhaOnNQnLsYJD1Ow7lTX1MtIZzTjOhRlzSviRWg==} + engines: {node: '>=10'} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@swc/core-win32-x64-msvc@1.7.3: + resolution: {integrity: sha512-jVQPbYrwcuueI4QB0fHC29SVrkFOBcfIspYDlgSoHnEz6tmLMqUy+txZUypY/ZH/KaK0HEY74JkzgbRC1S6LFQ==} + engines: {node: '>=10'} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@swc/core@1.7.3: + resolution: {integrity: sha512-HHAlbXjWI6Kl9JmmUW1LSygT1YbblXgj2UvvDzMkTBPRzYMhW6xchxdO8HbtMPtFYRt/EQq9u1z7j4ttRSrFsA==} + engines: {node: '>=10'} + requiresBuild: true + peerDependencies: + '@swc/helpers': '*' + peerDependenciesMeta: + '@swc/helpers': + optional: true + dependencies: + '@swc/counter': 0.1.3 + '@swc/types': 0.1.12 + optionalDependencies: + '@swc/core-darwin-arm64': 1.7.3 + '@swc/core-darwin-x64': 1.7.3 + '@swc/core-linux-arm-gnueabihf': 1.7.3 + '@swc/core-linux-arm64-gnu': 1.7.3 + '@swc/core-linux-arm64-musl': 1.7.3 + '@swc/core-linux-x64-gnu': 1.7.3 + '@swc/core-linux-x64-musl': 1.7.3 + '@swc/core-win32-arm64-msvc': 1.7.3 + '@swc/core-win32-ia32-msvc': 1.7.3 + '@swc/core-win32-x64-msvc': 1.7.3 + dev: true + + /@swc/counter@0.1.3: + resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==} + dev: true + /@swc/helpers@0.5.3: resolution: {integrity: sha512-FaruWX6KdudYloq1AHD/4nU+UsMTdNE8CKyrseXWEcgjDAbvkwJg2QGPAnfIJLIWsjZOSPLOAykK6fuYp4vp4A==} dependencies: tslib: 2.6.2 dev: false + /@swc/types@0.1.12: + resolution: {integrity: sha512-wBJA+SdtkbFhHjTMYH+dEH1y4VpfGdAc2Kw/LK09i9bXd/K6j6PkDcFCEzb6iVfZMkPRrl/q0e3toqTAJdkIVA==} + dependencies: + '@swc/counter': 0.1.3 + dev: true + /@testing-library/dom@8.18.1: resolution: {integrity: sha512-oEvsm2B/WtcHKE+IcEeeCqNU/ltFGaVyGbpcm4g/2ytuT49jrlH9x5qRKL/H3A6yfM4YAbSbC0ceT5+9CEXnLg==} engines: {node: '>=12'} @@ -3071,6 +3578,10 @@ packages: /@tsconfig/node16@1.0.3: resolution: {integrity: sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==} + /@types/argparse@1.0.38: + resolution: {integrity: sha512-ebDJ9b0e702Yr7pWgB0jzm+CX4Srzz8RcXtLJDJB+BSccqMa36uyH/zUsSYao5+BD1ytv3k3rPYCq4mAE1hsXA==} + dev: true + /@types/aria-query@4.2.2: resolution: {integrity: sha512-HnYpAE1Y6kRyKM/XkEuiRQhTHvkzMBurTHnpFLYLBGPIylZNPs9jJcuOOYWxPLJCSEtmZT0Y8rHDokKN7rRTig==} dev: true @@ -3148,7 +3659,7 @@ packages: resolution: {integrity: sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==} dependencies: '@types/eslint': 8.4.6 - '@types/estree': 0.0.51 + '@types/estree': 1.0.5 /@types/eslint-scope@3.7.7: resolution: {integrity: sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==} @@ -3160,7 +3671,7 @@ packages: /@types/eslint@8.4.6: resolution: {integrity: sha512-/fqTbjxyFUaYNO7VcW5g+4npmqVACz1bB7RTHYuLj+PRjw9hrCwrUXVQFpChUS0JsyEFvMZ7U/PfmvWgxJhI9g==} dependencies: - '@types/estree': 1.0.0 + '@types/estree': 1.0.5 '@types/json-schema': 7.0.15 /@types/eslint@8.56.2: @@ -3176,12 +3687,8 @@ packages: /@types/estree@0.0.51: resolution: {integrity: sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==} - /@types/estree@1.0.0: - resolution: {integrity: sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ==} - /@types/estree@1.0.5: resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} - dev: true /@types/express-serve-static-core@4.17.31: resolution: {integrity: sha512-DxMhY+NAsTwMMFHBTtJFNp5qiHKJ7TeqOo23zVEM9alT1Ml27Q3xcTH0xwxn7Q0BbMcVEJOs/7aQtUWupUQN3Q==} @@ -3397,7 +3904,36 @@ packages: transitivePeerDependencies: - supports-color - /@typescript-eslint/eslint-plugin@6.11.0(@typescript-eslint/parser@6.11.0)(eslint@8.53.0)(typescript@4.9.5): + /@typescript-eslint/eslint-plugin@6.11.0(@typescript-eslint/parser@6.11.0)(eslint@8.53.0)(typescript@4.9.5): + resolution: {integrity: sha512-uXnpZDc4VRjY4iuypDBKzW1rz9T5YBBK0snMn8MaTSNd2kMlj50LnLBABELjJiOL5YHk7ZD8hbSpI9ubzqYI0w==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + '@typescript-eslint/parser': ^6.0.0 || ^6.0.0-alpha + eslint: ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@eslint-community/regexpp': 4.10.0 + '@typescript-eslint/parser': 6.11.0(eslint@8.53.0)(typescript@4.9.5) + '@typescript-eslint/scope-manager': 6.11.0 + '@typescript-eslint/type-utils': 6.11.0(eslint@8.53.0)(typescript@4.9.5) + '@typescript-eslint/utils': 6.11.0(eslint@8.53.0)(typescript@4.9.5) + '@typescript-eslint/visitor-keys': 6.11.0 + debug: 4.3.4 + eslint: 8.53.0 + graphemer: 1.4.0 + ignore: 5.2.4 + natural-compare: 1.4.0 + semver: 7.5.4 + ts-api-utils: 1.0.3(typescript@4.9.5) + typescript: 4.9.5 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/eslint-plugin@6.11.0(@typescript-eslint/parser@6.11.0)(eslint@8.53.0)(typescript@5.5.4): resolution: {integrity: sha512-uXnpZDc4VRjY4iuypDBKzW1rz9T5YBBK0snMn8MaTSNd2kMlj50LnLBABELjJiOL5YHk7ZD8hbSpI9ubzqYI0w==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: @@ -3409,10 +3945,10 @@ packages: optional: true dependencies: '@eslint-community/regexpp': 4.10.0 - '@typescript-eslint/parser': 6.11.0(eslint@8.53.0)(typescript@4.9.5) + '@typescript-eslint/parser': 6.11.0(eslint@8.53.0)(typescript@5.5.4) '@typescript-eslint/scope-manager': 6.11.0 - '@typescript-eslint/type-utils': 6.11.0(eslint@8.53.0)(typescript@4.9.5) - '@typescript-eslint/utils': 6.11.0(eslint@8.53.0)(typescript@4.9.5) + '@typescript-eslint/type-utils': 6.11.0(eslint@8.53.0)(typescript@5.5.4) + '@typescript-eslint/utils': 6.11.0(eslint@8.53.0)(typescript@5.5.4) '@typescript-eslint/visitor-keys': 6.11.0 debug: 4.3.4 eslint: 8.53.0 @@ -3420,8 +3956,8 @@ packages: ignore: 5.2.4 natural-compare: 1.4.0 semver: 7.5.4 - ts-api-utils: 1.0.3(typescript@4.9.5) - typescript: 4.9.5 + ts-api-utils: 1.0.3(typescript@5.5.4) + typescript: 5.5.4 transitivePeerDependencies: - supports-color dev: true @@ -3478,6 +4014,27 @@ packages: - supports-color dev: true + /@typescript-eslint/parser@6.11.0(eslint@8.53.0)(typescript@5.5.4): + resolution: {integrity: sha512-+whEdjk+d5do5nxfxx73oanLL9ghKO3EwM9kBCkUtWMRwWuPaFv9ScuqlYfQ6pAD6ZiJhky7TZ2ZYhrMsfMxVQ==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + eslint: ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/scope-manager': 6.11.0 + '@typescript-eslint/types': 6.11.0 + '@typescript-eslint/typescript-estree': 6.11.0(typescript@5.5.4) + '@typescript-eslint/visitor-keys': 6.11.0 + debug: 4.3.4 + eslint: 8.53.0 + typescript: 5.5.4 + transitivePeerDependencies: + - supports-color + dev: true + /@typescript-eslint/scope-manager@5.38.0: resolution: {integrity: sha512-ByhHIuNyKD9giwkkLqzezZ9y5bALW8VNY6xXcP+VxoH4JBDKjU5WNnsiD4HJdglHECdV+lyaxhvQjTUbRboiTA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -3532,6 +4089,26 @@ packages: - supports-color dev: true + /@typescript-eslint/type-utils@6.11.0(eslint@8.53.0)(typescript@5.5.4): + resolution: {integrity: sha512-nA4IOXwZtqBjIoYrJcYxLRO+F9ri+leVGoJcMW1uqr4r1Hq7vW5cyWrA43lFbpRvQ9XgNrnfLpIkO3i1emDBIA==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + eslint: ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/typescript-estree': 6.11.0(typescript@5.5.4) + '@typescript-eslint/utils': 6.11.0(eslint@8.53.0)(typescript@5.5.4) + debug: 4.3.4 + eslint: 8.53.0 + ts-api-utils: 1.0.3(typescript@5.5.4) + typescript: 5.5.4 + transitivePeerDependencies: + - supports-color + dev: true + /@typescript-eslint/types@5.38.0: resolution: {integrity: sha512-HHu4yMjJ7i3Cb+8NUuRCdOGu2VMkfmKyIJsOr9PfkBVYLYrtMCK/Ap50Rpov+iKpxDTfnqvDbuPLgBE5FwUNfA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -3582,6 +4159,27 @@ packages: - supports-color dev: true + /@typescript-eslint/typescript-estree@6.11.0(typescript@5.5.4): + resolution: {integrity: sha512-Aezzv1o2tWJwvZhedzvD5Yv7+Lpu1by/U1LZ5gLc4tCx8jUmuSCMioPFRjliN/6SJIvY6HpTtJIWubKuYYYesQ==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/types': 6.11.0 + '@typescript-eslint/visitor-keys': 6.11.0 + debug: 4.3.4 + globby: 11.1.0 + is-glob: 4.0.3 + semver: 7.5.4 + ts-api-utils: 1.0.3(typescript@5.5.4) + typescript: 5.5.4 + transitivePeerDependencies: + - supports-color + dev: true + /@typescript-eslint/utils@5.38.0(eslint@8.53.0)(typescript@4.9.5): resolution: {integrity: sha512-6sdeYaBgk9Fh7N2unEXGz+D+som2QCQGPAf1SxrkEr+Z32gMreQ0rparXTNGRRfYUWk/JzbGdcM8NSSd6oqnTA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -3618,6 +4216,25 @@ packages: - typescript dev: true + /@typescript-eslint/utils@6.11.0(eslint@8.53.0)(typescript@5.5.4): + resolution: {integrity: sha512-p23ibf68fxoZy605dc0dQAEoUsoiNoP3MD9WQGiHLDuTSOuqoTsa4oAy+h3KDkTcxbbfOtUjb9h3Ta0gT4ug2g==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + eslint: ^7.0.0 || ^8.0.0 + dependencies: + '@eslint-community/eslint-utils': 4.4.0(eslint@8.53.0) + '@types/json-schema': 7.0.15 + '@types/semver': 7.5.5 + '@typescript-eslint/scope-manager': 6.11.0 + '@typescript-eslint/types': 6.11.0 + '@typescript-eslint/typescript-estree': 6.11.0(typescript@5.5.4) + eslint: 8.53.0 + semver: 7.5.4 + transitivePeerDependencies: + - supports-color + - typescript + dev: true + /@typescript-eslint/visitor-keys@5.38.0: resolution: {integrity: sha512-MxnrdIyArnTi+XyFLR+kt/uNAcdOnmT+879os7qDRI+EYySR4crXJq9BXPfRzzLGq0wgxkwidrCJ9WCAoacm1w==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -3636,6 +4253,77 @@ packages: /@ungap/structured-clone@1.2.0: resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==} + /@vitejs/plugin-react-swc@3.7.0(vite@4.5.3): + resolution: {integrity: sha512-yrknSb3Dci6svCd/qhHqhFPDSw0QtjumcqdKMoNNzmOl5lMXTTiqzjWtG4Qask2HdvvzaNgSunbQGet8/GrKdA==} + peerDependencies: + vite: ^4 || ^5 + dependencies: + '@swc/core': 1.7.3 + vite: 4.5.3(@types/node@16.11.59)(sass@1.54.9) + transitivePeerDependencies: + - '@swc/helpers' + dev: true + + /@volar/language-core@1.11.1: + resolution: {integrity: sha512-dOcNn3i9GgZAcJt43wuaEykSluAuOkQgzni1cuxLxTV0nJKanQztp7FxyswdRILaKH+P2XZMPRp2S4MV/pElCw==} + dependencies: + '@volar/source-map': 1.11.1 + dev: true + + /@volar/source-map@1.11.1: + resolution: {integrity: sha512-hJnOnwZ4+WT5iupLRnuzbULZ42L7BWWPMmruzwtLhJfpDVoZLjNBxHDi2sY2bgZXCKlpU5XcsMFoYrsQmPhfZg==} + dependencies: + muggle-string: 0.3.1 + dev: true + + /@volar/typescript@1.11.1: + resolution: {integrity: sha512-iU+t2mas/4lYierSnoFOeRFQUhAEMgsFuQxoxvwn5EdQopw43j+J27a4lt9LMInx1gLJBC6qL14WYGlgymaSMQ==} + dependencies: + '@volar/language-core': 1.11.1 + path-browserify: 1.0.1 + dev: true + + /@vue/compiler-core@3.4.34: + resolution: {integrity: sha512-Z0izUf32+wAnQewjHu+pQf1yw00EGOmevl1kE+ljjjMe7oEfpQ+BI3/JNK7yMB4IrUsqLDmPecUrpj3mCP+yJQ==} + dependencies: + '@babel/parser': 7.25.0 + '@vue/shared': 3.4.34 + entities: 4.5.0 + estree-walker: 2.0.2 + source-map-js: 1.2.0 + dev: true + + /@vue/compiler-dom@3.4.34: + resolution: {integrity: sha512-3PUOTS1h5cskdOJMExCu2TInXuM0j60DRPpSCJDqOCupCfUZCJoyQmKtRmA8EgDNZ5kcEE7vketamRZfrEuVDw==} + dependencies: + '@vue/compiler-core': 3.4.34 + '@vue/shared': 3.4.34 + dev: true + + /@vue/language-core@1.8.27(typescript@5.5.4): + resolution: {integrity: sha512-L8Kc27VdQserNaCUNiSFdDl9LWT24ly8Hpwf1ECy3aFb9m6bDhBGQYOujDm21N7EW3moKIOKEanQwe1q5BK+mA==} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@volar/language-core': 1.11.1 + '@volar/source-map': 1.11.1 + '@vue/compiler-dom': 3.4.34 + '@vue/shared': 3.4.34 + computeds: 0.0.1 + minimatch: 9.0.5 + muggle-string: 0.3.1 + path-browserify: 1.0.1 + typescript: 5.5.4 + vue-template-compiler: 2.7.16 + dev: true + + /@vue/shared@3.4.34: + resolution: {integrity: sha512-x5LmiRLpRsd9KTjAB8MPKf0CDPMcuItjP0gbNqFCIgL1I8iYp4zglhj9w9FPCdIbHG2M91RVeIbArFfFTz9I3A==} + dev: true + /@webassemblyjs/ast@1.11.1: resolution: {integrity: sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==} dependencies: @@ -3878,12 +4566,12 @@ packages: acorn: 8.11.3 dev: true - /acorn-jsx@5.3.2(acorn@8.10.0): + /acorn-jsx@5.3.2(acorn@8.11.3): resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} peerDependencies: acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 dependencies: - acorn: 8.10.0 + acorn: 8.11.3 /acorn-node@1.8.2: resolution: {integrity: sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A==} @@ -3905,11 +4593,6 @@ packages: engines: {node: '>=0.4.0'} hasBin: true - /acorn@8.10.0: - resolution: {integrity: sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==} - engines: {node: '>=0.4.0'} - hasBin: true - /acorn@8.11.3: resolution: {integrity: sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==} engines: {node: '>=0.4.0'} @@ -4802,6 +5485,13 @@ packages: resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==} engines: {node: '>= 12'} + /commander@9.5.0: + resolution: {integrity: sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==} + engines: {node: ^12.20.0 || >=14} + requiresBuild: true + dev: true + optional: true + /common-path-prefix@3.0.0: resolution: {integrity: sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w==} @@ -4839,6 +5529,10 @@ packages: transitivePeerDependencies: - supports-color + /computeds@0.0.1: + resolution: {integrity: sha512-7CEBgcMjVmitjYo5q8JTJVra6X5mQ20uTThdK+0kR7UEaDrAWEQcRiBtWJzga4eRpP6afNwwLsX2SET2JhVB1Q==} + dev: true + /concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} @@ -5227,6 +5921,10 @@ packages: resolution: {integrity: sha512-CAdX5Q3YW3Gclyo5Vpqkgpj8fSdLQcRuzfX6mC6Phy0nfJ0eGYOeS7m4mt2plDWLAtA4TqTakvbboHvUxfe4iA==} dev: false + /de-indent@1.0.2: + resolution: {integrity: sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==} + dev: true + /debug@2.6.9: resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} peerDependencies: @@ -5596,6 +6294,11 @@ packages: /entities@2.2.0: resolution: {integrity: sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==, tarball: https://registry.yarnpkg.com/entities/-/entities-2.2.0.tgz} + /entities@4.5.0: + resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} + engines: {node: '>=0.12'} + dev: true + /error-ex@1.3.2: resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} dependencies: @@ -5727,6 +6430,36 @@ packages: is-date-object: 1.0.5 is-symbol: 1.0.4 + /esbuild@0.18.20: + resolution: {integrity: sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==} + engines: {node: '>=12'} + hasBin: true + requiresBuild: true + optionalDependencies: + '@esbuild/android-arm': 0.18.20 + '@esbuild/android-arm64': 0.18.20 + '@esbuild/android-x64': 0.18.20 + '@esbuild/darwin-arm64': 0.18.20 + '@esbuild/darwin-x64': 0.18.20 + '@esbuild/freebsd-arm64': 0.18.20 + '@esbuild/freebsd-x64': 0.18.20 + '@esbuild/linux-arm': 0.18.20 + '@esbuild/linux-arm64': 0.18.20 + '@esbuild/linux-ia32': 0.18.20 + '@esbuild/linux-loong64': 0.18.20 + '@esbuild/linux-mips64el': 0.18.20 + '@esbuild/linux-ppc64': 0.18.20 + '@esbuild/linux-riscv64': 0.18.20 + '@esbuild/linux-s390x': 0.18.20 + '@esbuild/linux-x64': 0.18.20 + '@esbuild/netbsd-x64': 0.18.20 + '@esbuild/openbsd-x64': 0.18.20 + '@esbuild/sunos-x64': 0.18.20 + '@esbuild/win32-arm64': 0.18.20 + '@esbuild/win32-ia32': 0.18.20 + '@esbuild/win32-x64': 0.18.20 + dev: true + /escalade@3.1.1: resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==} engines: {node: '>=6'} @@ -6139,6 +6872,14 @@ packages: dependencies: eslint: 8.53.0 + /eslint-plugin-react-refresh@0.4.9(eslint@8.53.0): + resolution: {integrity: sha512-QK49YrBAo5CLNLseZ7sZgvgTy21E6NEw22eZqc4teZfH8pxV3yXc9XXOYfUI6JNpw7mfHNkAeWtBxrTyykB6HA==} + peerDependencies: + eslint: '>=7' + dependencies: + eslint: 8.53.0 + dev: true + /eslint-plugin-react@7.33.2(eslint@8.53.0): resolution: {integrity: sha512-73QQMKALArI8/7xGLNI/3LylrEYrlKZSb5C9+q3OtOewTnMQi5cT+aE9E41sLCmli3I9PGGmD1yiZydyo4FEPw==} engines: {node: '>=4'} @@ -6283,8 +7024,8 @@ packages: resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dependencies: - acorn: 8.10.0 - acorn-jsx: 5.3.2(acorn@8.10.0) + acorn: 8.11.3 + acorn-jsx: 5.3.2(acorn@8.11.3) eslint-visitor-keys: 3.4.3 /esprima@4.0.1: @@ -6315,6 +7056,10 @@ packages: /estree-walker@1.0.1: resolution: {integrity: sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==} + /estree-walker@2.0.2: + resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} + dev: true + /esutils@2.0.3: resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} engines: {node: '>=0.10.0'} @@ -6633,6 +7378,15 @@ packages: jsonfile: 6.1.0 universalify: 2.0.0 + /fs-extra@7.0.1: + resolution: {integrity: sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==} + engines: {node: '>=6 <7 || >=8'} + dependencies: + graceful-fs: 4.2.11 + jsonfile: 4.0.0 + universalify: 0.1.2 + dev: true + /fs-extra@9.1.0: resolution: {integrity: sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==} engines: {node: '>=10'} @@ -7099,6 +7853,11 @@ packages: parent-module: 1.0.1 resolve-from: 4.0.0 + /import-lazy@4.0.0: + resolution: {integrity: sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==} + engines: {node: '>=8'} + dev: true + /import-local@3.1.0: resolution: {integrity: sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==} engines: {node: '>=8'} @@ -8101,6 +8860,12 @@ packages: engines: {node: '>=6'} hasBin: true + /jsonfile@4.0.0: + resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==} + optionalDependencies: + graceful-fs: 4.2.11 + dev: true + /jsonfile@6.1.0: resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} dependencies: @@ -8153,6 +8918,10 @@ packages: resolution: {integrity: sha512-pJiBpiXMbt7dkzXe8Ghj/u4FfXOOa98fPW+bihOJ4SjnoijweJrNThJfd3ifXpXhREjpoF2mZVH1GfS9LV3kHQ==} engines: {node: '>= 8'} + /kolorist@1.8.0: + resolution: {integrity: sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==} + dev: true + /language-subtag-registry@0.3.22: resolution: {integrity: sha512-tN0MCzyWnoz/4nHS6uxdlFWoUZT7ABptwKPQ52Ea7URk6vll88bWBVhodtnlfEuCcKWNGoc+uGbw1cwa9IKh/w==} @@ -8271,6 +9040,14 @@ packages: resolution: {integrity: sha512-ff3BX/tSioo+XojX4MOsOMhJw0nZoUEF011LX8g8d3gvjVbxd89cCio4BCXronjxcTUIJUoqKEUA+n4CqvvRPw==} dev: true + /lodash.get@4.4.2: + resolution: {integrity: sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==} + dev: true + + /lodash.isequal@4.5.0: + resolution: {integrity: sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==} + dev: true + /lodash.memoize@4.1.2: resolution: {integrity: sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==} @@ -8324,6 +9101,12 @@ packages: dependencies: sourcemap-codec: 1.4.8 + /magic-string@0.30.11: + resolution: {integrity: sha512-+Wri9p0QHMy+545hKww7YAu5NyzF8iomPL/RQazugQ9+Ez4Ic3mERMd8ZTX5rfK944j+560ZJi8iAwgak1Ac7A==} + dependencies: + '@jridgewell/sourcemap-codec': 1.5.0 + dev: true + /make-dir@3.1.0: resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==} engines: {node: '>=8'} @@ -8465,6 +9248,13 @@ packages: dependencies: brace-expansion: 2.0.1 + /minimatch@9.0.5: + resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} + engines: {node: '>=16 || 14 >=14.17'} + dependencies: + brace-expansion: 2.0.1 + dev: true + /minimist-options@4.1.0: resolution: {integrity: sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==} engines: {node: '>= 6'} @@ -8492,6 +9282,10 @@ packages: /ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + /muggle-string@0.3.1: + resolution: {integrity: sha512-ckmWDJjphvd/FvZawgygcUeQCxzvohjFO5RxTjj4eq8kw359gFF3E1brjfI+viLMxss5JrHTDRHZvu2/tuy0Qg==} + dev: true + /multicast-dns@7.2.5: resolution: {integrity: sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg==} hasBin: true @@ -8504,6 +9298,12 @@ packages: engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true + /nanoid@3.3.7: + resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + dev: true + /natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} @@ -8844,6 +9644,10 @@ packages: no-case: 3.0.4 tslib: 2.6.2 + /path-browserify@1.0.1: + resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} + dev: true + /path-exists@3.0.0: resolution: {integrity: sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==} engines: {node: '>=4'} @@ -8884,6 +9688,10 @@ packages: /picocolors@1.0.0: resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} + /picocolors@1.0.1: + resolution: {integrity: sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==} + dev: true + /picomatch@2.3.1: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} engines: {node: '>=8.6'} @@ -9621,6 +10429,15 @@ packages: picocolors: 1.0.0 source-map-js: 1.0.2 + /postcss@8.4.40: + resolution: {integrity: sha512-YF2kKIUzAofPMpfH6hOi2cGnv/HrUlfucspc7pDyvv7kGdqXrfj8SCl/t8owkEgKEuu8ZcRjSOxFxVLqwChZ2Q==} + engines: {node: ^10 || ^12 || >=14} + dependencies: + nanoid: 3.3.7 + picocolors: 1.0.1 + source-map-js: 1.2.0 + dev: true + /prelude-ls@1.1.2: resolution: {integrity: sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==} engines: {node: '>= 0.8.0'} @@ -10327,6 +11144,13 @@ packages: resolution: {integrity: sha512-J1l+Zxxp4XK3LUDZ9m60LRJF/mAe4z6a4xyabPHk7pvK5t35dACV32iIjJDFeWZFfZlO29w6SZ67knR0tHzJtQ==} engines: {node: '>=10'} + /resolve@1.19.0: + resolution: {integrity: sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==} + dependencies: + is-core-module: 2.10.0 + path-parse: 1.0.7 + dev: true + /resolve@1.22.1: resolution: {integrity: sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==} hasBin: true @@ -10394,6 +11218,14 @@ packages: optionalDependencies: fsevents: 2.3.3 + /rollup@3.29.4: + resolution: {integrity: sha512-oWzmBZwvYrU0iJHtDmhsm662rC15FRXmcjCk1xD771dFDx5jJ02ufAQQTn0etB2emNk4J9EZg/yWKpsn9BWGRw==} + engines: {node: '>=14.18.0', npm: '>=8.0.0'} + hasBin: true + optionalDependencies: + fsevents: 2.3.3 + dev: true + /run-applescript@5.0.0: resolution: {integrity: sha512-XcT5rBksx1QdIhlFOCtgZkB99ZEouFZ1E2Kc2LHqNW13U3/74YGdkQRmThTwxy4QIyookibDKYZOPqX//6BlAg==} engines: {node: '>=12'} @@ -10736,6 +11568,11 @@ packages: resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==} engines: {node: '>=0.10.0'} + /source-map-js@1.2.0: + resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==} + engines: {node: '>=0.10.0'} + dev: true + /source-map-loader@3.0.1(webpack@5.74.0): resolution: {integrity: sha512-Vp1UsfyPvgujKQzi4pyDiTOnE3E4H+yHvkVRN3c/9PJmQS4CQJExvcDvaX/D+RV+xQben9HJ56jMJS3CgUeWyA==} engines: {node: '>= 12.13.0'} @@ -11324,6 +12161,11 @@ packages: resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} engines: {node: '>=0.6'} + /tosource@2.0.0-alpha.3: + resolution: {integrity: sha512-KAB2lrSS48y91MzFPFuDg4hLbvDiyTjOVgaK7Erw+5AmZXNq4sFRVn8r6yxSLuNs15PaokrDRpS61ERY9uZOug==} + engines: {node: '>=10'} + dev: true + /tough-cookie@4.1.2: resolution: {integrity: sha512-G9fqXWoYFZgTc2z8Q5zaHy/vJMjm+WV0AkAeHxVCQiEB1b+dGvWzFW6QV07cY5jQ5gRkeid2qIkzkxUnmoQZUQ==} engines: {node: '>=6'} @@ -11361,6 +12203,15 @@ packages: typescript: 4.9.5 dev: true + /ts-api-utils@1.0.3(typescript@5.5.4): + resolution: {integrity: sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg==} + engines: {node: '>=16.13.0'} + peerDependencies: + typescript: '>=4.2.0' + dependencies: + typescript: 5.5.4 + dev: true + /ts-node@10.9.1(@types/node@16.11.59)(typescript@4.9.5): resolution: {integrity: sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==} hasBin: true @@ -11513,6 +12364,18 @@ packages: engines: {node: '>=4.2.0'} hasBin: true + /typescript@5.4.2: + resolution: {integrity: sha512-+2/g0Fds1ERlP6JsakQQDXjZdZMM+rqpamFZJEKh4kwTIn3iDkgKtby0CeNd5ATNZ4Ry1ax15TMx0W2V+miizQ==} + engines: {node: '>=14.17'} + hasBin: true + dev: true + + /typescript@5.5.4: + resolution: {integrity: sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==} + engines: {node: '>=14.17'} + hasBin: true + dev: true + /unbox-primitive@1.0.2: resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==} dependencies: @@ -11566,6 +12429,11 @@ packages: dependencies: crypto-random-string: 2.0.0 + /universalify@0.1.2: + resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} + engines: {node: '>= 4.0.0'} + dev: true + /universalify@0.2.0: resolution: {integrity: sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==} engines: {node: '>= 4.0.0'} @@ -11598,7 +12466,7 @@ packages: dependencies: browserslist: 4.22.2 escalade: 3.1.1 - picocolors: 1.0.0 + picocolors: 1.0.1 dev: true /update-browserslist-db@1.0.9(browserslist@4.21.4): @@ -11675,15 +12543,101 @@ packages: spdx-expression-parse: 3.0.1 dev: true + /validator@13.12.0: + resolution: {integrity: sha512-c1Q0mCiPlgdTVVVIJIrBuxNicYE+t/7oKeI9MWLj3fh/uq2Pxh/3eeWbVZ4OcGW1TUf53At0njHw5SMdA3tmMg==} + engines: {node: '>= 0.10'} + dev: true + /vary@1.1.2: resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} engines: {node: '>= 0.8'} + /vite-plugin-dts@3.9.1(@types/node@16.11.59)(rollup@2.79.0)(typescript@5.5.4)(vite@4.5.3): + resolution: {integrity: sha512-rVp2KM9Ue22NGWB8dNtWEr+KekN3rIgz1tWD050QnRGlriUCmaDwa7qA5zDEjbXg5lAXhYMSBJtx3q3hQIJZSg==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + typescript: '*' + vite: '*' + peerDependenciesMeta: + vite: + optional: true + dependencies: + '@microsoft/api-extractor': 7.43.0(@types/node@16.11.59) + '@rollup/pluginutils': 5.1.0(rollup@2.79.0) + '@vue/language-core': 1.8.27(typescript@5.5.4) + debug: 4.3.4 + kolorist: 1.8.0 + magic-string: 0.30.11 + typescript: 5.5.4 + vite: 4.5.3(@types/node@16.11.59)(sass@1.54.9) + vue-tsc: 1.8.27(typescript@5.5.4) + transitivePeerDependencies: + - '@types/node' + - rollup + - supports-color + dev: true + + /vite@4.5.3(@types/node@16.11.59)(sass@1.54.9): + resolution: {integrity: sha512-kQL23kMeX92v3ph7IauVkXkikdDRsYMGTVl5KY2E9OY4ONLvkHf04MDTbnfo6NKxZiDLWzVpP5oTa8hQD8U3dg==} + engines: {node: ^14.18.0 || >=16.0.0} + hasBin: true + peerDependencies: + '@types/node': '>= 14' + less: '*' + lightningcss: ^1.21.0 + sass: '*' + stylus: '*' + sugarss: '*' + terser: ^5.4.0 + peerDependenciesMeta: + '@types/node': + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + dependencies: + '@types/node': 16.11.59 + esbuild: 0.18.20 + postcss: 8.4.40 + rollup: 3.29.4 + sass: 1.54.9 + optionalDependencies: + fsevents: 2.3.3 + dev: true + /void-elements@3.1.0: resolution: {integrity: sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==} engines: {node: '>=0.10.0'} dev: false + /vue-template-compiler@2.7.16: + resolution: {integrity: sha512-AYbUWAJHLGGQM7+cNTELw+KsOG9nl2CnSv467WobS5Cv9uk3wFcnr1Etsz2sEIHEZvw1U+o9mRlEO6QbZvUPGQ==} + dependencies: + de-indent: 1.0.2 + he: 1.2.0 + dev: true + + /vue-tsc@1.8.27(typescript@5.5.4): + resolution: {integrity: sha512-WesKCAZCRAbmmhuGl3+VrdWItEvfoFIPXOvUJkjULi+x+6G/Dy69yO3TBRJDr9eUlmsNAwVmxsNZxvHKzbkKdg==} + hasBin: true + peerDependencies: + typescript: '*' + dependencies: + '@volar/typescript': 1.11.1 + '@vue/language-core': 1.8.27(typescript@5.5.4) + semver: 7.5.4 + typescript: 5.5.4 + dev: true + /w3c-hr-time@1.0.2: resolution: {integrity: sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==} dependencies: @@ -12320,6 +13274,18 @@ packages: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} + /z-schema@5.0.5: + resolution: {integrity: sha512-D7eujBWkLa3p2sIpJA0d1pr7es+a7m0vFAnZLlCEKq/Ij2k0MLi9Br2UPxoxdYystm5K1yeBGzub0FlYUEWj2Q==} + engines: {node: '>=8.0.0'} + hasBin: true + dependencies: + lodash.get: 4.4.2 + lodash.isequal: 4.5.0 + validator: 13.12.0 + optionalDependencies: + commander: 9.5.0 + dev: true + /zustand@4.1.1(react@18.2.0): resolution: {integrity: sha512-h4F3WMqsZgvvaE0n3lThx4MM81Ls9xebjvrABNzf5+jb3/03YjNTSgZXeyrvXDArMeV9untvWXRw1tY+ntPYbA==} engines: {node: '>=12.7.0'} diff --git a/ui/src/components/CardBadge/index.scss b/ui/src/components/CardBadge/index.scss new file mode 100644 index 000000000..30c30cb1a --- /dev/null +++ b/ui/src/components/CardBadge/index.scss @@ -0,0 +1,36 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +.badge-card { + width: 194px; + margin: 12px; + + .label { + position: absolute; + top: 1rem; + right: 1rem; + } +} + + +@media screen and (max-width: 768px) { + .badge-card { + width: 163.5px; + } +} diff --git a/ui/src/components/CardBadge/index.tsx b/ui/src/components/CardBadge/index.tsx new file mode 100644 index 000000000..b8dde5029 --- /dev/null +++ b/ui/src/components/CardBadge/index.tsx @@ -0,0 +1,51 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import { useTranslation } from 'react-i18next'; +import { FC } from 'react'; +import { Card, Badge } from 'react-bootstrap'; + +import { formatCount } from '@/utils'; + +import './index.scss'; + +interface IProps { + data: any; + badgePill: boolean; +} + +const Index: FC = ({ data, badgePill }) => { + const { t } = useTranslation('translation', { keyPrefix: 'badges' }); + console.log(data); + return ( + + + + 0 + + +
Nice Question
+
+ {t('x_awarded', { number: formatCount(16) })} +
+
+
+ ); +}; + +export default Index; diff --git a/ui/src/components/SideNav/index.tsx b/ui/src/components/SideNav/index.tsx index 6c53fe597..327d7b38e 100644 --- a/ui/src/components/SideNav/index.tsx +++ b/ui/src/components/SideNav/index.tsx @@ -73,6 +73,11 @@ const Index: FC = () => { {t('header.nav.user')} + + + {t('header.nav.badges')} + + {can_revision || userInfo?.role_id === 2 ? ( <>
diff --git a/ui/src/components/index.ts b/ui/src/components/index.ts index fdf052d39..44dc15160 100644 --- a/ui/src/components/index.ts +++ b/ui/src/components/index.ts @@ -60,6 +60,7 @@ import ImgViewer from './ImgViewer'; import SideNav from './SideNav'; import PluginRender from './PluginRender'; import HighlightText from './HighlightText'; +import CardBadge from './CardBadge'; export { Avatar, @@ -107,5 +108,6 @@ export { SideNav, PluginRender, HighlightText, + CardBadge, }; export type { EditorRef, JSONSchema, UISchema }; diff --git a/ui/src/pages/Badges/Detail/index.tsx b/ui/src/pages/Badges/Detail/index.tsx new file mode 100644 index 000000000..7a192a13a --- /dev/null +++ b/ui/src/pages/Badges/Detail/index.tsx @@ -0,0 +1,97 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { Card, Badge, Row, Col } from 'react-bootstrap'; +import { useTranslation } from 'react-i18next'; +import { Link } from 'react-router-dom'; + +import { Avatar, FormatTime } from '@/components'; +import { usePageTags } from '@/hooks'; +import { formatCount } from '@/utils'; + +const Index = () => { + const { t } = useTranslation('translation', { keyPrefix: 'badges' }); + + usePageTags({ + title: t('title'), + }); + + return ( +
+

{t('title')}

+ + + +
+
Support Expert
+
+ This badge is granted for achieving the expert level of our + community support programme. This certifies that the recipient has + demonstrated a high level of skills and abilities to manage and + support multiple communities/instances. +
+
{t('can_earn_multiple')}
+
+ + {t('x_awarded', { number: formatCount(16) })} + + + {t('earned_x', { number: 2 })} + +
+
+
+
+ + {[0, 1, 2, 3, 4, 5, 6].map((item) => { + return ( + + +
+ + + +
+ + username + +
+ 980 {t('x_reputation', { keyPrefix: 'personal' })} +
+
+
+ + How to `go test` all tests in my project? + + + ); + })} +
+
+ ); +}; + +export default Index; diff --git a/ui/src/pages/Badges/index.tsx b/ui/src/pages/Badges/index.tsx new file mode 100644 index 000000000..84784c53e --- /dev/null +++ b/ui/src/pages/Badges/index.tsx @@ -0,0 +1,45 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { useTranslation } from 'react-i18next'; + +import { CardBadge } from '@/components'; +import { usePageTags } from '@/hooks'; + +const Index = () => { + const { t } = useTranslation('translation', { keyPrefix: 'badges' }); + + usePageTags({ + title: t('title'), + }); + + return ( +
+

{t('title')}

+
Community Badges
+
+ {[0, 1, 2, 3, 4, 5, 6].map((item) => { + return ; + })} +
+
+ ); +}; + +export default Index; diff --git a/ui/src/pages/Users/Personal/components/Alert/index.tsx b/ui/src/pages/Users/Personal/components/Alert/index.tsx index 65fd157b0..8bde01f28 100644 --- a/ui/src/pages/Users/Personal/components/Alert/index.tsx +++ b/ui/src/pages/Users/Personal/components/Alert/index.tsx @@ -18,7 +18,7 @@ */ import { memo, FC, useState } from 'react'; -import { Alert, Col } from 'react-bootstrap'; +import { Alert } from 'react-bootstrap'; interface Props { data; @@ -27,17 +27,16 @@ const Index: FC = ({ data }) => { const [show, setShow] = useState(Boolean(data)); return ( - - { - setShow(false); - }}> -
- - + { + setShow(false); + }}> +
+ ); }; diff --git a/ui/src/pages/Users/Personal/components/Badges/index.tsx b/ui/src/pages/Users/Personal/components/Badges/index.tsx new file mode 100644 index 000000000..a7bd3e58a --- /dev/null +++ b/ui/src/pages/Users/Personal/components/Badges/index.tsx @@ -0,0 +1,43 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { FC } from 'react'; + +import { CardBadge } from '@/components'; + +interface IProps { + data: any[]; + visible: boolean; +} + +const Index: FC = ({ data, visible }) => { + console.log(data); + if (!visible) { + return null; + } + return ( +
+ {[0, 1, 2, 3, 4, 5, 6].map((item) => { + return ; + })} +
+ ); +}; + +export default Index; diff --git a/ui/src/pages/Users/Personal/components/NavBar/index.tsx b/ui/src/pages/Users/Personal/components/NavBar/index.tsx index d201a579d..5f4360ed8 100644 --- a/ui/src/pages/Users/Personal/components/NavBar/index.tsx +++ b/ui/src/pages/Users/Personal/components/NavBar/index.tsx @@ -59,6 +59,11 @@ const list = [ path: '/votes', name: 'votes', }, + { + role: 'self', // Only visible to author + path: '/badges', + name: 'badges', + }, ]; const Index: FC = ({ slug, tabName = 'overview', isSelf }) => { const { t } = useTranslation('translation', { keyPrefix: 'personal' }); diff --git a/ui/src/pages/Users/Personal/components/Overview/index.tsx b/ui/src/pages/Users/Personal/components/Overview/index.tsx index 3d5c7360c..4f2280618 100644 --- a/ui/src/pages/Users/Personal/components/Overview/index.tsx +++ b/ui/src/pages/Users/Personal/components/Overview/index.tsx @@ -19,6 +19,7 @@ import { FC, memo } from 'react'; import { useTranslation } from 'react-i18next'; +import { Row, Col } from 'react-bootstrap'; import TopList from '../TopList'; @@ -37,26 +38,31 @@ const Index: FC = ({ visible, introduction, data }) => {
{t('about_me')}
{introduction ? (
) : ( -
{t('about_me_empty')}
+
{t('about_me_empty')}
)} - {data?.answer?.length > 0 && ( - <> -
{t('top_answers')}
- - - )} - - {data?.question?.length > 0 && ( - <> -
{t('top_questions')}
- - - )} + + + {data?.answer?.length > 0 && ( + <> +
{t('top_answers')}
+ + + )} + + + {data?.question?.length > 0 && ( + <> +
{t('top_questions')}
+ + + )} + +
); }; diff --git a/ui/src/pages/Users/Personal/components/TopList/index.tsx b/ui/src/pages/Users/Personal/components/TopList/index.tsx index ad9044fa6..045534288 100644 --- a/ui/src/pages/Users/Personal/components/TopList/index.tsx +++ b/ui/src/pages/Users/Personal/components/TopList/index.tsx @@ -31,14 +31,13 @@ interface Props { const Index: FC = ({ data, type }) => { const { t } = useTranslation('translation', { keyPrefix: 'personal' }); return ( -
    - {data?.map((item) => { +
      + {data?.map((item, index) => { return (
    1. = ({ data, type }) => { {type === 'answer' ? item.question_info.title : item.title} -
      +
      {item.vote_count} {t('votes', { keyPrefix: 'counts' })} -
      - {type === 'question' && ( -
      0 ? 'text-success' : '' - }`}> - {Number(item.accepted_answer_id) > 0 ? ( - - ) : ( - - )} - - {' '} - {item.answer_count} {t('answers', { keyPrefix: 'counts' })} - -
      - )} + {type === 'question' && ( +
      0 ? 'text-success' : '' + }`}> + {Number(item.accepted_answer_id) > 0 ? ( + + ) : ( + + )} + + + {' '} + {item.answer_count} {t('answers', { keyPrefix: 'counts' })} + +
      + )} - {type === 'answer' && item.accepted === 2 && ( -
      - - {t('accepted')} -
      - )} + {type === 'answer' && item.accepted === 2 && ( +
      + + {t('accepted')} +
      + )} +
    2. ); })} diff --git a/ui/src/pages/Users/Personal/components/index.ts b/ui/src/pages/Users/Personal/components/index.ts index c2c3ec79b..0a9baab1f 100644 --- a/ui/src/pages/Users/Personal/components/index.ts +++ b/ui/src/pages/Users/Personal/components/index.ts @@ -28,6 +28,7 @@ import Reputation from './Reputation'; import Comments from './Comments'; import Votes from './Votes'; import Answers from './Answers'; +import Badges from './Badges'; export { Alert, @@ -41,4 +42,5 @@ export { Comments, Votes, Answers, + Badges, }; diff --git a/ui/src/pages/Users/Personal/index.tsx b/ui/src/pages/Users/Personal/index.tsx index 2d205a42a..96704b858 100644 --- a/ui/src/pages/Users/Personal/index.tsx +++ b/ui/src/pages/Users/Personal/index.tsx @@ -43,6 +43,7 @@ import { Comments, Answers, Votes, + Badges, } from './components'; const Personal: FC = () => { @@ -79,36 +80,30 @@ const Personal: FC = () => { return (
      - {userInfo?.status !== 'normal' && userInfo?.status_msg && ( - - )} - - - - - {isSelf && ( -
      - - {t('edit_profile')} - -
      + + {userInfo?.status !== 'normal' && userInfo?.status_msg && ( + )} - -
      - - - +
      + + {isSelf && ( +
      + + {t('edit_profile')} + +
      + )} +
      + + + { + {!list?.length && !isLoading && } {count > 0 && ( @@ -135,20 +131,20 @@ const Personal: FC = () => { />
      )} - - -
      {t('stats')}
      - {userInfo?.created_at && ( + + {tabName === 'overview' && ( <> -
      - -
      -
      - -
      +
      {t('stats')}
      + {userInfo?.created_at && ( +
      + + {t('comma')}{' '} + +
      + )} )} diff --git a/ui/src/router/routes.ts b/ui/src/router/routes.ts index bd4a0c796..aa31266eb 100644 --- a/ui/src/router/routes.ts +++ b/ui/src/router/routes.ts @@ -217,6 +217,14 @@ const routes: RouteNode[] = [ path: 'review', page: 'pages/Review', }, + { + path: '/badges', + page: 'pages/Badges/index', + }, + { + path: '/badges/:badge_id', + page: 'pages/Badges/Detail/index', + }, ], }, { From 6973a0045e8e7c5e66667449f2969e5d5378d256 Mon Sep 17 00:00:00 2001 From: shuai Date: Tue, 6 Aug 2024 10:50:58 +0800 Subject: [PATCH 20/64] fix: personal add recent bages ui --- i18n/en_US.yaml | 1 + ui/src/components/CardBadge/index.tsx | 11 +++++++---- .../Users/Personal/components/Overview/index.tsx | 12 +++++++++++- 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/i18n/en_US.yaml b/i18n/en_US.yaml index f9787eda3..09596b783 100644 --- a/i18n/en_US.yaml +++ b/i18n/en_US.yaml @@ -1598,6 +1598,7 @@ ui: x_votes: votes received x_answers: answers x_questions: questions + recent_badges: Recent Badges install: title: Installation next: Next diff --git a/ui/src/components/CardBadge/index.tsx b/ui/src/components/CardBadge/index.tsx index b8dde5029..9251b38ce 100644 --- a/ui/src/components/CardBadge/index.tsx +++ b/ui/src/components/CardBadge/index.tsx @@ -27,9 +27,10 @@ import './index.scss'; interface IProps { data: any; badgePill: boolean; + showAwardedCount?: boolean; } -const Index: FC = ({ data, badgePill }) => { +const Index: FC = ({ data, badgePill, showAwardedCount = false }) => { const { t } = useTranslation('translation', { keyPrefix: 'badges' }); console.log(data); return ( @@ -40,9 +41,11 @@ const Index: FC = ({ data, badgePill }) => {
      Nice Question
      -
      - {t('x_awarded', { number: formatCount(16) })} -
      + {showAwardedCount && ( +
      + {t('x_awarded', { number: formatCount(16) })} +
      + )} ); diff --git a/ui/src/pages/Users/Personal/components/Overview/index.tsx b/ui/src/pages/Users/Personal/components/Overview/index.tsx index 4f2280618..384689dcb 100644 --- a/ui/src/pages/Users/Personal/components/Overview/index.tsx +++ b/ui/src/pages/Users/Personal/components/Overview/index.tsx @@ -21,6 +21,7 @@ import { FC, memo } from 'react'; import { useTranslation } from 'react-i18next'; import { Row, Col } from 'react-bootstrap'; +import { CardBadge } from '@/components'; import TopList from '../TopList'; interface Props { @@ -54,7 +55,7 @@ const Index: FC = ({ visible, introduction, data }) => { )} - + {data?.question?.length > 0 && ( <>
      {t('top_questions')}
      @@ -63,6 +64,15 @@ const Index: FC = ({ visible, introduction, data }) => { )} + +
      +
      {t('recent_badges')}
      +
      + {[0, 1, 2, 3, 4, 5, 6].map((item) => { + return ; + })} +
      +
); }; From 8534079687e6257fed2eb3dae07fa116b1a8c590 Mon Sep 17 00:00:00 2001 From: shuai Date: Tue, 6 Aug 2024 15:56:30 +0800 Subject: [PATCH 21/64] fix: replace share params shareUserId to share --- ui/src/components/Share/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/src/components/Share/index.tsx b/ui/src/components/Share/index.tsx index 203a72d6d..392d1fbdd 100644 --- a/ui/src/components/Share/index.tsx +++ b/ui/src/components/Share/index.tsx @@ -46,7 +46,7 @@ const Index: FC = ({ type, qid, aid, title }) => { ? `${BASE_ORIGIN}/questions/${qid}` : `${BASE_ORIGIN}/questions/${qid}/${aid}`; if (user.id) { - baseUrl = `${baseUrl}?shareUserId=${user.username}`; + baseUrl = `${baseUrl}?share=${user.username}`; } const closeShare = () => { From 3f1f31581f6bb0b9165b607a04ab95234dd09bdd Mon Sep 17 00:00:00 2001 From: shuai Date: Thu, 8 Aug 2024 18:28:37 +0800 Subject: [PATCH 22/64] fix: badge list and badge detail docking api --- ui/package.json | 3 +- ui/pnpm-lock.yaml | 988 +----------------- ui/src/common/interface.ts | 32 + ui/src/components/CardBadge/index.tsx | 46 +- ui/src/index.scss | 10 + .../Badges/Detail/components/Badge/index.tsx | 93 ++ .../Detail/components/HeaderLoader/index.tsx | 49 + .../Badges/Detail/components/Loader/index.tsx | 55 + ui/src/pages/Badges/Detail/index.tsx | 131 ++- ui/src/pages/Badges/index.tsx | 21 +- .../Personal/components/Badges/index.tsx | 7 +- .../Personal/components/Overview/index.tsx | 7 +- ui/src/services/client/badges.ts | 64 ++ ui/src/services/client/index.ts | 1 + 14 files changed, 448 insertions(+), 1059 deletions(-) create mode 100644 ui/src/pages/Badges/Detail/components/Badge/index.tsx create mode 100644 ui/src/pages/Badges/Detail/components/HeaderLoader/index.tsx create mode 100644 ui/src/pages/Badges/Detail/components/Loader/index.tsx create mode 100644 ui/src/services/client/badges.ts diff --git a/ui/package.json b/ui/package.json index 0ae727e23..2344125da 100644 --- a/ui/package.json +++ b/ui/package.json @@ -45,8 +45,7 @@ "react-router-dom": "^6.22.3", "semver": "^7.3.8", "swr": "^1.3.0", - "zustand": "^4.1.1", - "demo": "workspace:*" + "zustand": "^4.1.1" }, "devDependencies": { "@commitlint/cli": "^17.0.3", diff --git a/ui/pnpm-lock.yaml b/ui/pnpm-lock.yaml index 50fdae8f5..0fd9c0f1a 100644 --- a/ui/pnpm-lock.yaml +++ b/ui/pnpm-lock.yaml @@ -44,9 +44,6 @@ importers: dayjs: specifier: ^1.11.5 version: 1.11.5 - demo: - specifier: workspace:* - version: link:src/plugins/demo diff: specifier: ^5.1.0 version: 5.1.0 @@ -232,52 +229,6 @@ importers: specifier: ^0.8.0 version: 0.8.0 - src/plugins/demo: - dependencies: - react: - specifier: ^18.2.0 - version: 18.2.0 - react-bootstrap: - specifier: ^2.10.0 - version: 2.10.0(@types/react@18.0.20)(react-dom@18.2.0)(react@18.2.0) - react-dom: - specifier: ^18.2.0 - version: 18.2.0(react@18.2.0) - react-i18next: - specifier: ^11.18.3 - version: 11.18.6(i18next@21.9.2)(react-dom@18.2.0)(react@18.2.0) - devDependencies: - '@modyfi/vite-plugin-yaml': - specifier: ^1.1.0 - version: 1.1.0(rollup@2.79.0)(vite@4.5.3) - '@typescript-eslint/eslint-plugin': - specifier: ^6.0.0 - version: 6.11.0(@typescript-eslint/parser@6.11.0)(eslint@8.53.0)(typescript@5.5.4) - '@typescript-eslint/parser': - specifier: ^6.0.0 - version: 6.11.0(eslint@8.53.0)(typescript@5.5.4) - '@vitejs/plugin-react-swc': - specifier: ^3.3.2 - version: 3.7.0(vite@4.5.3) - eslint: - specifier: ^8.45.0 - version: 8.53.0 - eslint-plugin-react-hooks: - specifier: ^4.6.0 - version: 4.6.0(eslint@8.53.0) - eslint-plugin-react-refresh: - specifier: ^0.4.3 - version: 0.4.9(eslint@8.53.0) - typescript: - specifier: ^5.0.2 - version: 5.5.4 - vite: - specifier: ^4.4.5 - version: 4.5.3(@types/node@16.11.59)(sass@1.54.9) - vite-plugin-dts: - specifier: ^3.9.1 - version: 3.9.1(@types/node@16.11.59)(rollup@2.79.0)(typescript@5.5.4)(vite@4.5.3) - packages: /@aashutoshrathi/word-wrap@1.2.6: @@ -598,14 +549,6 @@ packages: dependencies: '@babel/types': 7.19.0 - /@babel/parser@7.25.0: - resolution: {integrity: sha512-CzdIU9jdP0dg7HdyB+bHvDJGagUv+qtzZt5rYCWwW6tITNqV9odjp6Qu41gkG0ca5UfdDUWrKkiAnHHdGRnOrA==} - engines: {node: '>=6.0.0'} - hasBin: true - dependencies: - '@babel/types': 7.23.6 - dev: true - /@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.18.6(@babel/core@7.19.1): resolution: {integrity: sha512-Dgxsyg54Fx1d4Nge8UnvTrED63vrwOdPmyvPzlNN/boaliRP54pm3pGzZD1SJUwrBA+Cs/xdG8kXX6Mn/RfISQ==} engines: {node: '>=6.9.0'} @@ -2248,204 +2191,6 @@ packages: postcss: 8.4.16 postcss-selector-parser: 6.0.10 - /@esbuild/android-arm64@0.18.20: - resolution: {integrity: sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==} - engines: {node: '>=12'} - cpu: [arm64] - os: [android] - requiresBuild: true - dev: true - optional: true - - /@esbuild/android-arm@0.18.20: - resolution: {integrity: sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==} - engines: {node: '>=12'} - cpu: [arm] - os: [android] - requiresBuild: true - dev: true - optional: true - - /@esbuild/android-x64@0.18.20: - resolution: {integrity: sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==} - engines: {node: '>=12'} - cpu: [x64] - os: [android] - requiresBuild: true - dev: true - optional: true - - /@esbuild/darwin-arm64@0.18.20: - resolution: {integrity: sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==} - engines: {node: '>=12'} - cpu: [arm64] - os: [darwin] - requiresBuild: true - dev: true - optional: true - - /@esbuild/darwin-x64@0.18.20: - resolution: {integrity: sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==} - engines: {node: '>=12'} - cpu: [x64] - os: [darwin] - requiresBuild: true - dev: true - optional: true - - /@esbuild/freebsd-arm64@0.18.20: - resolution: {integrity: sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==} - engines: {node: '>=12'} - cpu: [arm64] - os: [freebsd] - requiresBuild: true - dev: true - optional: true - - /@esbuild/freebsd-x64@0.18.20: - resolution: {integrity: sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==} - engines: {node: '>=12'} - cpu: [x64] - os: [freebsd] - requiresBuild: true - dev: true - optional: true - - /@esbuild/linux-arm64@0.18.20: - resolution: {integrity: sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==} - engines: {node: '>=12'} - cpu: [arm64] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /@esbuild/linux-arm@0.18.20: - resolution: {integrity: sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==} - engines: {node: '>=12'} - cpu: [arm] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /@esbuild/linux-ia32@0.18.20: - resolution: {integrity: sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==} - engines: {node: '>=12'} - cpu: [ia32] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /@esbuild/linux-loong64@0.18.20: - resolution: {integrity: sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==} - engines: {node: '>=12'} - cpu: [loong64] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /@esbuild/linux-mips64el@0.18.20: - resolution: {integrity: sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==} - engines: {node: '>=12'} - cpu: [mips64el] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /@esbuild/linux-ppc64@0.18.20: - resolution: {integrity: sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==} - engines: {node: '>=12'} - cpu: [ppc64] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /@esbuild/linux-riscv64@0.18.20: - resolution: {integrity: sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==} - engines: {node: '>=12'} - cpu: [riscv64] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /@esbuild/linux-s390x@0.18.20: - resolution: {integrity: sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==} - engines: {node: '>=12'} - cpu: [s390x] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /@esbuild/linux-x64@0.18.20: - resolution: {integrity: sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==} - engines: {node: '>=12'} - cpu: [x64] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /@esbuild/netbsd-x64@0.18.20: - resolution: {integrity: sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==} - engines: {node: '>=12'} - cpu: [x64] - os: [netbsd] - requiresBuild: true - dev: true - optional: true - - /@esbuild/openbsd-x64@0.18.20: - resolution: {integrity: sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==} - engines: {node: '>=12'} - cpu: [x64] - os: [openbsd] - requiresBuild: true - dev: true - optional: true - - /@esbuild/sunos-x64@0.18.20: - resolution: {integrity: sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==} - engines: {node: '>=12'} - cpu: [x64] - os: [sunos] - requiresBuild: true - dev: true - optional: true - - /@esbuild/win32-arm64@0.18.20: - resolution: {integrity: sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==} - engines: {node: '>=12'} - cpu: [arm64] - os: [win32] - requiresBuild: true - dev: true - optional: true - - /@esbuild/win32-ia32@0.18.20: - resolution: {integrity: sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==} - engines: {node: '>=12'} - cpu: [ia32] - os: [win32] - requiresBuild: true - dev: true - optional: true - - /@esbuild/win32-x64@0.18.20: - resolution: {integrity: sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==} - engines: {node: '>=12'} - cpu: [x64] - os: [win32] - requiresBuild: true - dev: true - optional: true - /@eslint-community/eslint-utils@4.4.0(eslint@8.53.0): resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -2960,63 +2705,6 @@ packages: '@lezer/lr': 1.4.0 dev: false - /@microsoft/api-extractor-model@7.28.13(@types/node@16.11.59): - resolution: {integrity: sha512-39v/JyldX4MS9uzHcdfmjjfS6cYGAoXV+io8B5a338pkHiSt+gy2eXQ0Q7cGFJ7quSa1VqqlMdlPrB6sLR/cAw==} - dependencies: - '@microsoft/tsdoc': 0.14.2 - '@microsoft/tsdoc-config': 0.16.2 - '@rushstack/node-core-library': 4.0.2(@types/node@16.11.59) - transitivePeerDependencies: - - '@types/node' - dev: true - - /@microsoft/api-extractor@7.43.0(@types/node@16.11.59): - resolution: {integrity: sha512-GFhTcJpB+MI6FhvXEI9b2K0snulNLWHqC/BbcJtyNYcKUiw7l3Lgis5ApsYncJ0leALX7/of4XfmXk+maT111w==} - hasBin: true - dependencies: - '@microsoft/api-extractor-model': 7.28.13(@types/node@16.11.59) - '@microsoft/tsdoc': 0.14.2 - '@microsoft/tsdoc-config': 0.16.2 - '@rushstack/node-core-library': 4.0.2(@types/node@16.11.59) - '@rushstack/rig-package': 0.5.2 - '@rushstack/terminal': 0.10.0(@types/node@16.11.59) - '@rushstack/ts-command-line': 4.19.1(@types/node@16.11.59) - lodash: 4.17.21 - minimatch: 3.0.4 - resolve: 1.22.1 - semver: 7.5.4 - source-map: 0.6.1 - typescript: 5.4.2 - transitivePeerDependencies: - - '@types/node' - dev: true - - /@microsoft/tsdoc-config@0.16.2: - resolution: {integrity: sha512-OGiIzzoBLgWWR0UdRJX98oYO+XKGf7tiK4Zk6tQ/E4IJqGCe7dvkTvgDZV5cFJUzLGDOjeAXrnZoA6QkVySuxw==} - dependencies: - '@microsoft/tsdoc': 0.14.2 - ajv: 6.12.6 - jju: 1.4.0 - resolve: 1.19.0 - dev: true - - /@microsoft/tsdoc@0.14.2: - resolution: {integrity: sha512-9b8mPpKrfeGRuhFH5iO1iwCLeIIsV6+H1sRfxbkoGXIyQE2BTsPd9zqSqQJ+pv5sJ/hT5M1zvOFL02MnEezFug==} - dev: true - - /@modyfi/vite-plugin-yaml@1.1.0(rollup@2.79.0)(vite@4.5.3): - resolution: {integrity: sha512-L26xfzkSo1yamODCAtk/ipVlL6OEw2bcJ92zunyHu8zxi7+meV0zefA9xscRMDCsMY8xL3C3wi3DhMiPxcbxbw==} - peerDependencies: - vite: ^3.2.7 || ^4.0.5 || ^5.0.5 - dependencies: - '@rollup/pluginutils': 5.1.0(rollup@2.79.0) - js-yaml: 4.1.0 - tosource: 2.0.0-alpha.3 - vite: 4.5.3(@types/node@16.11.59)(sass@1.54.9) - transitivePeerDependencies: - - rollup - dev: true - /@nicolo-ribaudo/eslint-scope-5-internals@5.1.1-v1: resolution: {integrity: sha512-54/JRvkLIzzDWshCWfuhadfrfZVPiElY8Fcgmg1HroEly/EDSszzhBAsarCux+D/kOslTRquNzuyGSmUSTTHGg==} dependencies: @@ -3188,72 +2876,9 @@ packages: picomatch: 2.3.1 rollup: 2.79.0 - /@rollup/pluginutils@5.1.0(rollup@2.79.0): - resolution: {integrity: sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g==} - engines: {node: '>=14.0.0'} - peerDependencies: - rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 - peerDependenciesMeta: - rollup: - optional: true - dependencies: - '@types/estree': 1.0.5 - estree-walker: 2.0.2 - picomatch: 2.3.1 - rollup: 2.79.0 - dev: true - /@rushstack/eslint-patch@1.2.0: resolution: {integrity: sha512-sXo/qW2/pAcmT43VoRKOJbDOfV3cYpq3szSVfIThQXNt+E4DfKj361vaAt3c88U5tPUxzEswam7GW48PJqtKAg==} - /@rushstack/node-core-library@4.0.2(@types/node@16.11.59): - resolution: {integrity: sha512-hyES82QVpkfQMeBMteQUnrhASL/KHPhd7iJ8euduwNJG4mu2GSOKybf0rOEjOm1Wz7CwJEUm9y0yD7jg2C1bfg==} - peerDependencies: - '@types/node': '*' - peerDependenciesMeta: - '@types/node': - optional: true - dependencies: - '@types/node': 16.11.59 - fs-extra: 7.0.1 - import-lazy: 4.0.0 - jju: 1.4.0 - resolve: 1.22.1 - semver: 7.5.4 - z-schema: 5.0.5 - dev: true - - /@rushstack/rig-package@0.5.2: - resolution: {integrity: sha512-mUDecIJeH3yYGZs2a48k+pbhM6JYwWlgjs2Ca5f2n1G2/kgdgP9D/07oglEGf6mRyXEnazhEENeYTSNDRCwdqA==} - dependencies: - resolve: 1.22.1 - strip-json-comments: 3.1.1 - dev: true - - /@rushstack/terminal@0.10.0(@types/node@16.11.59): - resolution: {integrity: sha512-UbELbXnUdc7EKwfH2sb8ChqNgapUOdqcCIdQP4NGxBpTZV2sQyeekuK3zmfQSa/MN+/7b4kBogl2wq0vpkpYGw==} - peerDependencies: - '@types/node': '*' - peerDependenciesMeta: - '@types/node': - optional: true - dependencies: - '@rushstack/node-core-library': 4.0.2(@types/node@16.11.59) - '@types/node': 16.11.59 - supports-color: 8.1.1 - dev: true - - /@rushstack/ts-command-line@4.19.1(@types/node@16.11.59): - resolution: {integrity: sha512-J7H768dgcpG60d7skZ5uSSwyCZs/S2HrWP1Ds8d1qYAyaaeJmpmmLr9BVw97RjFzmQPOYnoXcKA4GkqDCkduQg==} - dependencies: - '@rushstack/terminal': 0.10.0(@types/node@16.11.59) - '@types/argparse': 1.0.38 - argparse: 1.0.10 - string-argv: 0.3.2 - transitivePeerDependencies: - - '@types/node' - dev: true - /@sinclair/typebox@0.24.42: resolution: {integrity: sha512-d+2AtrHGyWek2u2ITF0lHRIv6Tt7X0dEHW+0rP+5aDCEjC3fiN2RBjrLD0yU0at52BcZbRGxLbAtXiR0hFCjYw==} @@ -3370,141 +2995,12 @@ packages: transitivePeerDependencies: - supports-color - /@swc/core-darwin-arm64@1.7.3: - resolution: {integrity: sha512-CTkHa6MJdov9t41vuV2kmQIMu+Q19LrEHGIR/UiJYH06SC/sOu35ZZH8DyfLp9ZoaCn21gwgWd61ixOGQlwzTw==} - engines: {node: '>=10'} - cpu: [arm64] - os: [darwin] - requiresBuild: true - dev: true - optional: true - - /@swc/core-darwin-x64@1.7.3: - resolution: {integrity: sha512-mun623y6rCoZ2EFIYfIRqXYRFufJOopoYSJcxYhZUrfTpAvQ1zLngjQpWCUU1krggXR2U0PQj+ls0DfXUTraNg==} - engines: {node: '>=10'} - cpu: [x64] - os: [darwin] - requiresBuild: true - dev: true - optional: true - - /@swc/core-linux-arm-gnueabihf@1.7.3: - resolution: {integrity: sha512-4Jz4UcIcvZNMp9qoHbBx35bo3rjt8hpYLPqnR4FFq6gkAsJIMFC56UhRZwdEQoDuYiOFMBnnrsg31Fyo6YQypA==} - engines: {node: '>=10'} - cpu: [arm] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /@swc/core-linux-arm64-gnu@1.7.3: - resolution: {integrity: sha512-p+U/M/oqV7HC4erQ5TVWHhJU1984QD+wQBPxslAYq751bOQGm0R/mXK42GjugqjnR6yYrAiwKKbpq4iWVXNePA==} - engines: {node: '>=10'} - cpu: [arm64] - os: [linux] - libc: [glibc] - requiresBuild: true - dev: true - optional: true - - /@swc/core-linux-arm64-musl@1.7.3: - resolution: {integrity: sha512-s6VzyaJwaRGTi2mz2h6Ywxfmgpkc69IxhuMzl+sl34plH0V0RgnZDm14HoCGIKIzRk4+a2EcBV1ZLAfWmPACQg==} - engines: {node: '>=10'} - cpu: [arm64] - os: [linux] - libc: [musl] - requiresBuild: true - dev: true - optional: true - - /@swc/core-linux-x64-gnu@1.7.3: - resolution: {integrity: sha512-IrFY48C356Z2dU2pjYg080yvMXzmSV3Lmm/Wna4cfcB1nkVLjWsuYwwRAk9CY7E19c+q8N1sMNggubAUDYoX2g==} - engines: {node: '>=10'} - cpu: [x64] - os: [linux] - libc: [glibc] - requiresBuild: true - dev: true - optional: true - - /@swc/core-linux-x64-musl@1.7.3: - resolution: {integrity: sha512-qoLgxBlBnnyUEDu5vmRQqX90h9jldU1JXI96e6eh2d1gJyKRA0oSK7xXmTzorv1fGHiHulv9qiJOUG+g6uzJWg==} - engines: {node: '>=10'} - cpu: [x64] - os: [linux] - libc: [musl] - requiresBuild: true - dev: true - optional: true - - /@swc/core-win32-arm64-msvc@1.7.3: - resolution: {integrity: sha512-OAd7jVVJ7nb0Ev80VAa1aeK+FldPeC4eZ35H4Qn6EICzIz0iqJo2T33qLKkSZiZEBKSoF4KcwrqYfkjLOp5qWg==} - engines: {node: '>=10'} - cpu: [arm64] - os: [win32] - requiresBuild: true - dev: true - optional: true - - /@swc/core-win32-ia32-msvc@1.7.3: - resolution: {integrity: sha512-31+Le1NyfSnILFV9+AhxfFOG0DK0272MNhbIlbcv4w/iqpjkhaOnNQnLsYJD1Ow7lTX1MtIZzTjOhRlzSviRWg==} - engines: {node: '>=10'} - cpu: [ia32] - os: [win32] - requiresBuild: true - dev: true - optional: true - - /@swc/core-win32-x64-msvc@1.7.3: - resolution: {integrity: sha512-jVQPbYrwcuueI4QB0fHC29SVrkFOBcfIspYDlgSoHnEz6tmLMqUy+txZUypY/ZH/KaK0HEY74JkzgbRC1S6LFQ==} - engines: {node: '>=10'} - cpu: [x64] - os: [win32] - requiresBuild: true - dev: true - optional: true - - /@swc/core@1.7.3: - resolution: {integrity: sha512-HHAlbXjWI6Kl9JmmUW1LSygT1YbblXgj2UvvDzMkTBPRzYMhW6xchxdO8HbtMPtFYRt/EQq9u1z7j4ttRSrFsA==} - engines: {node: '>=10'} - requiresBuild: true - peerDependencies: - '@swc/helpers': '*' - peerDependenciesMeta: - '@swc/helpers': - optional: true - dependencies: - '@swc/counter': 0.1.3 - '@swc/types': 0.1.12 - optionalDependencies: - '@swc/core-darwin-arm64': 1.7.3 - '@swc/core-darwin-x64': 1.7.3 - '@swc/core-linux-arm-gnueabihf': 1.7.3 - '@swc/core-linux-arm64-gnu': 1.7.3 - '@swc/core-linux-arm64-musl': 1.7.3 - '@swc/core-linux-x64-gnu': 1.7.3 - '@swc/core-linux-x64-musl': 1.7.3 - '@swc/core-win32-arm64-msvc': 1.7.3 - '@swc/core-win32-ia32-msvc': 1.7.3 - '@swc/core-win32-x64-msvc': 1.7.3 - dev: true - - /@swc/counter@0.1.3: - resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==} - dev: true - /@swc/helpers@0.5.3: resolution: {integrity: sha512-FaruWX6KdudYloq1AHD/4nU+UsMTdNE8CKyrseXWEcgjDAbvkwJg2QGPAnfIJLIWsjZOSPLOAykK6fuYp4vp4A==} dependencies: tslib: 2.6.2 dev: false - /@swc/types@0.1.12: - resolution: {integrity: sha512-wBJA+SdtkbFhHjTMYH+dEH1y4VpfGdAc2Kw/LK09i9bXd/K6j6PkDcFCEzb6iVfZMkPRrl/q0e3toqTAJdkIVA==} - dependencies: - '@swc/counter': 0.1.3 - dev: true - /@testing-library/dom@8.18.1: resolution: {integrity: sha512-oEvsm2B/WtcHKE+IcEeeCqNU/ltFGaVyGbpcm4g/2ytuT49jrlH9x5qRKL/H3A6yfM4YAbSbC0ceT5+9CEXnLg==} engines: {node: '>=12'} @@ -3578,10 +3074,6 @@ packages: /@tsconfig/node16@1.0.3: resolution: {integrity: sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==} - /@types/argparse@1.0.38: - resolution: {integrity: sha512-ebDJ9b0e702Yr7pWgB0jzm+CX4Srzz8RcXtLJDJB+BSccqMa36uyH/zUsSYao5+BD1ytv3k3rPYCq4mAE1hsXA==} - dev: true - /@types/aria-query@4.2.2: resolution: {integrity: sha512-HnYpAE1Y6kRyKM/XkEuiRQhTHvkzMBurTHnpFLYLBGPIylZNPs9jJcuOOYWxPLJCSEtmZT0Y8rHDokKN7rRTig==} dev: true @@ -3892,48 +3384,19 @@ packages: dependencies: '@typescript-eslint/parser': 5.38.0(eslint@8.53.0)(typescript@4.9.5) '@typescript-eslint/scope-manager': 5.38.0 - '@typescript-eslint/type-utils': 5.38.0(eslint@8.53.0)(typescript@4.9.5) - '@typescript-eslint/utils': 5.38.0(eslint@8.53.0)(typescript@4.9.5) - debug: 4.3.4 - eslint: 8.53.0 - ignore: 5.2.4 - regexpp: 3.2.0 - semver: 7.5.4 - tsutils: 3.21.0(typescript@4.9.5) - typescript: 4.9.5 - transitivePeerDependencies: - - supports-color - - /@typescript-eslint/eslint-plugin@6.11.0(@typescript-eslint/parser@6.11.0)(eslint@8.53.0)(typescript@4.9.5): - resolution: {integrity: sha512-uXnpZDc4VRjY4iuypDBKzW1rz9T5YBBK0snMn8MaTSNd2kMlj50LnLBABELjJiOL5YHk7ZD8hbSpI9ubzqYI0w==} - engines: {node: ^16.0.0 || >=18.0.0} - peerDependencies: - '@typescript-eslint/parser': ^6.0.0 || ^6.0.0-alpha - eslint: ^7.0.0 || ^8.0.0 - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - dependencies: - '@eslint-community/regexpp': 4.10.0 - '@typescript-eslint/parser': 6.11.0(eslint@8.53.0)(typescript@4.9.5) - '@typescript-eslint/scope-manager': 6.11.0 - '@typescript-eslint/type-utils': 6.11.0(eslint@8.53.0)(typescript@4.9.5) - '@typescript-eslint/utils': 6.11.0(eslint@8.53.0)(typescript@4.9.5) - '@typescript-eslint/visitor-keys': 6.11.0 + '@typescript-eslint/type-utils': 5.38.0(eslint@8.53.0)(typescript@4.9.5) + '@typescript-eslint/utils': 5.38.0(eslint@8.53.0)(typescript@4.9.5) debug: 4.3.4 eslint: 8.53.0 - graphemer: 1.4.0 ignore: 5.2.4 - natural-compare: 1.4.0 + regexpp: 3.2.0 semver: 7.5.4 - ts-api-utils: 1.0.3(typescript@4.9.5) + tsutils: 3.21.0(typescript@4.9.5) typescript: 4.9.5 transitivePeerDependencies: - supports-color - dev: true - /@typescript-eslint/eslint-plugin@6.11.0(@typescript-eslint/parser@6.11.0)(eslint@8.53.0)(typescript@5.5.4): + /@typescript-eslint/eslint-plugin@6.11.0(@typescript-eslint/parser@6.11.0)(eslint@8.53.0)(typescript@4.9.5): resolution: {integrity: sha512-uXnpZDc4VRjY4iuypDBKzW1rz9T5YBBK0snMn8MaTSNd2kMlj50LnLBABELjJiOL5YHk7ZD8hbSpI9ubzqYI0w==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: @@ -3945,10 +3408,10 @@ packages: optional: true dependencies: '@eslint-community/regexpp': 4.10.0 - '@typescript-eslint/parser': 6.11.0(eslint@8.53.0)(typescript@5.5.4) + '@typescript-eslint/parser': 6.11.0(eslint@8.53.0)(typescript@4.9.5) '@typescript-eslint/scope-manager': 6.11.0 - '@typescript-eslint/type-utils': 6.11.0(eslint@8.53.0)(typescript@5.5.4) - '@typescript-eslint/utils': 6.11.0(eslint@8.53.0)(typescript@5.5.4) + '@typescript-eslint/type-utils': 6.11.0(eslint@8.53.0)(typescript@4.9.5) + '@typescript-eslint/utils': 6.11.0(eslint@8.53.0)(typescript@4.9.5) '@typescript-eslint/visitor-keys': 6.11.0 debug: 4.3.4 eslint: 8.53.0 @@ -3956,8 +3419,8 @@ packages: ignore: 5.2.4 natural-compare: 1.4.0 semver: 7.5.4 - ts-api-utils: 1.0.3(typescript@5.5.4) - typescript: 5.5.4 + ts-api-utils: 1.0.3(typescript@4.9.5) + typescript: 4.9.5 transitivePeerDependencies: - supports-color dev: true @@ -4014,27 +3477,6 @@ packages: - supports-color dev: true - /@typescript-eslint/parser@6.11.0(eslint@8.53.0)(typescript@5.5.4): - resolution: {integrity: sha512-+whEdjk+d5do5nxfxx73oanLL9ghKO3EwM9kBCkUtWMRwWuPaFv9ScuqlYfQ6pAD6ZiJhky7TZ2ZYhrMsfMxVQ==} - engines: {node: ^16.0.0 || >=18.0.0} - peerDependencies: - eslint: ^7.0.0 || ^8.0.0 - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - dependencies: - '@typescript-eslint/scope-manager': 6.11.0 - '@typescript-eslint/types': 6.11.0 - '@typescript-eslint/typescript-estree': 6.11.0(typescript@5.5.4) - '@typescript-eslint/visitor-keys': 6.11.0 - debug: 4.3.4 - eslint: 8.53.0 - typescript: 5.5.4 - transitivePeerDependencies: - - supports-color - dev: true - /@typescript-eslint/scope-manager@5.38.0: resolution: {integrity: sha512-ByhHIuNyKD9giwkkLqzezZ9y5bALW8VNY6xXcP+VxoH4JBDKjU5WNnsiD4HJdglHECdV+lyaxhvQjTUbRboiTA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -4089,26 +3531,6 @@ packages: - supports-color dev: true - /@typescript-eslint/type-utils@6.11.0(eslint@8.53.0)(typescript@5.5.4): - resolution: {integrity: sha512-nA4IOXwZtqBjIoYrJcYxLRO+F9ri+leVGoJcMW1uqr4r1Hq7vW5cyWrA43lFbpRvQ9XgNrnfLpIkO3i1emDBIA==} - engines: {node: ^16.0.0 || >=18.0.0} - peerDependencies: - eslint: ^7.0.0 || ^8.0.0 - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - dependencies: - '@typescript-eslint/typescript-estree': 6.11.0(typescript@5.5.4) - '@typescript-eslint/utils': 6.11.0(eslint@8.53.0)(typescript@5.5.4) - debug: 4.3.4 - eslint: 8.53.0 - ts-api-utils: 1.0.3(typescript@5.5.4) - typescript: 5.5.4 - transitivePeerDependencies: - - supports-color - dev: true - /@typescript-eslint/types@5.38.0: resolution: {integrity: sha512-HHu4yMjJ7i3Cb+8NUuRCdOGu2VMkfmKyIJsOr9PfkBVYLYrtMCK/Ap50Rpov+iKpxDTfnqvDbuPLgBE5FwUNfA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -4159,27 +3581,6 @@ packages: - supports-color dev: true - /@typescript-eslint/typescript-estree@6.11.0(typescript@5.5.4): - resolution: {integrity: sha512-Aezzv1o2tWJwvZhedzvD5Yv7+Lpu1by/U1LZ5gLc4tCx8jUmuSCMioPFRjliN/6SJIvY6HpTtJIWubKuYYYesQ==} - engines: {node: ^16.0.0 || >=18.0.0} - peerDependencies: - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - dependencies: - '@typescript-eslint/types': 6.11.0 - '@typescript-eslint/visitor-keys': 6.11.0 - debug: 4.3.4 - globby: 11.1.0 - is-glob: 4.0.3 - semver: 7.5.4 - ts-api-utils: 1.0.3(typescript@5.5.4) - typescript: 5.5.4 - transitivePeerDependencies: - - supports-color - dev: true - /@typescript-eslint/utils@5.38.0(eslint@8.53.0)(typescript@4.9.5): resolution: {integrity: sha512-6sdeYaBgk9Fh7N2unEXGz+D+som2QCQGPAf1SxrkEr+Z32gMreQ0rparXTNGRRfYUWk/JzbGdcM8NSSd6oqnTA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -4216,25 +3617,6 @@ packages: - typescript dev: true - /@typescript-eslint/utils@6.11.0(eslint@8.53.0)(typescript@5.5.4): - resolution: {integrity: sha512-p23ibf68fxoZy605dc0dQAEoUsoiNoP3MD9WQGiHLDuTSOuqoTsa4oAy+h3KDkTcxbbfOtUjb9h3Ta0gT4ug2g==} - engines: {node: ^16.0.0 || >=18.0.0} - peerDependencies: - eslint: ^7.0.0 || ^8.0.0 - dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.53.0) - '@types/json-schema': 7.0.15 - '@types/semver': 7.5.5 - '@typescript-eslint/scope-manager': 6.11.0 - '@typescript-eslint/types': 6.11.0 - '@typescript-eslint/typescript-estree': 6.11.0(typescript@5.5.4) - eslint: 8.53.0 - semver: 7.5.4 - transitivePeerDependencies: - - supports-color - - typescript - dev: true - /@typescript-eslint/visitor-keys@5.38.0: resolution: {integrity: sha512-MxnrdIyArnTi+XyFLR+kt/uNAcdOnmT+879os7qDRI+EYySR4crXJq9BXPfRzzLGq0wgxkwidrCJ9WCAoacm1w==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -4253,77 +3635,6 @@ packages: /@ungap/structured-clone@1.2.0: resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==} - /@vitejs/plugin-react-swc@3.7.0(vite@4.5.3): - resolution: {integrity: sha512-yrknSb3Dci6svCd/qhHqhFPDSw0QtjumcqdKMoNNzmOl5lMXTTiqzjWtG4Qask2HdvvzaNgSunbQGet8/GrKdA==} - peerDependencies: - vite: ^4 || ^5 - dependencies: - '@swc/core': 1.7.3 - vite: 4.5.3(@types/node@16.11.59)(sass@1.54.9) - transitivePeerDependencies: - - '@swc/helpers' - dev: true - - /@volar/language-core@1.11.1: - resolution: {integrity: sha512-dOcNn3i9GgZAcJt43wuaEykSluAuOkQgzni1cuxLxTV0nJKanQztp7FxyswdRILaKH+P2XZMPRp2S4MV/pElCw==} - dependencies: - '@volar/source-map': 1.11.1 - dev: true - - /@volar/source-map@1.11.1: - resolution: {integrity: sha512-hJnOnwZ4+WT5iupLRnuzbULZ42L7BWWPMmruzwtLhJfpDVoZLjNBxHDi2sY2bgZXCKlpU5XcsMFoYrsQmPhfZg==} - dependencies: - muggle-string: 0.3.1 - dev: true - - /@volar/typescript@1.11.1: - resolution: {integrity: sha512-iU+t2mas/4lYierSnoFOeRFQUhAEMgsFuQxoxvwn5EdQopw43j+J27a4lt9LMInx1gLJBC6qL14WYGlgymaSMQ==} - dependencies: - '@volar/language-core': 1.11.1 - path-browserify: 1.0.1 - dev: true - - /@vue/compiler-core@3.4.34: - resolution: {integrity: sha512-Z0izUf32+wAnQewjHu+pQf1yw00EGOmevl1kE+ljjjMe7oEfpQ+BI3/JNK7yMB4IrUsqLDmPecUrpj3mCP+yJQ==} - dependencies: - '@babel/parser': 7.25.0 - '@vue/shared': 3.4.34 - entities: 4.5.0 - estree-walker: 2.0.2 - source-map-js: 1.2.0 - dev: true - - /@vue/compiler-dom@3.4.34: - resolution: {integrity: sha512-3PUOTS1h5cskdOJMExCu2TInXuM0j60DRPpSCJDqOCupCfUZCJoyQmKtRmA8EgDNZ5kcEE7vketamRZfrEuVDw==} - dependencies: - '@vue/compiler-core': 3.4.34 - '@vue/shared': 3.4.34 - dev: true - - /@vue/language-core@1.8.27(typescript@5.5.4): - resolution: {integrity: sha512-L8Kc27VdQserNaCUNiSFdDl9LWT24ly8Hpwf1ECy3aFb9m6bDhBGQYOujDm21N7EW3moKIOKEanQwe1q5BK+mA==} - peerDependencies: - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - dependencies: - '@volar/language-core': 1.11.1 - '@volar/source-map': 1.11.1 - '@vue/compiler-dom': 3.4.34 - '@vue/shared': 3.4.34 - computeds: 0.0.1 - minimatch: 9.0.5 - muggle-string: 0.3.1 - path-browserify: 1.0.1 - typescript: 5.5.4 - vue-template-compiler: 2.7.16 - dev: true - - /@vue/shared@3.4.34: - resolution: {integrity: sha512-x5LmiRLpRsd9KTjAB8MPKf0CDPMcuItjP0gbNqFCIgL1I8iYp4zglhj9w9FPCdIbHG2M91RVeIbArFfFTz9I3A==} - dev: true - /@webassemblyjs/ast@1.11.1: resolution: {integrity: sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==} dependencies: @@ -5485,13 +4796,6 @@ packages: resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==} engines: {node: '>= 12'} - /commander@9.5.0: - resolution: {integrity: sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==} - engines: {node: ^12.20.0 || >=14} - requiresBuild: true - dev: true - optional: true - /common-path-prefix@3.0.0: resolution: {integrity: sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w==} @@ -5529,10 +4833,6 @@ packages: transitivePeerDependencies: - supports-color - /computeds@0.0.1: - resolution: {integrity: sha512-7CEBgcMjVmitjYo5q8JTJVra6X5mQ20uTThdK+0kR7UEaDrAWEQcRiBtWJzga4eRpP6afNwwLsX2SET2JhVB1Q==} - dev: true - /concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} @@ -5921,10 +5221,6 @@ packages: resolution: {integrity: sha512-CAdX5Q3YW3Gclyo5Vpqkgpj8fSdLQcRuzfX6mC6Phy0nfJ0eGYOeS7m4mt2plDWLAtA4TqTakvbboHvUxfe4iA==} dev: false - /de-indent@1.0.2: - resolution: {integrity: sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==} - dev: true - /debug@2.6.9: resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} peerDependencies: @@ -6294,11 +5590,6 @@ packages: /entities@2.2.0: resolution: {integrity: sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==, tarball: https://registry.yarnpkg.com/entities/-/entities-2.2.0.tgz} - /entities@4.5.0: - resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} - engines: {node: '>=0.12'} - dev: true - /error-ex@1.3.2: resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} dependencies: @@ -6430,36 +5721,6 @@ packages: is-date-object: 1.0.5 is-symbol: 1.0.4 - /esbuild@0.18.20: - resolution: {integrity: sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==} - engines: {node: '>=12'} - hasBin: true - requiresBuild: true - optionalDependencies: - '@esbuild/android-arm': 0.18.20 - '@esbuild/android-arm64': 0.18.20 - '@esbuild/android-x64': 0.18.20 - '@esbuild/darwin-arm64': 0.18.20 - '@esbuild/darwin-x64': 0.18.20 - '@esbuild/freebsd-arm64': 0.18.20 - '@esbuild/freebsd-x64': 0.18.20 - '@esbuild/linux-arm': 0.18.20 - '@esbuild/linux-arm64': 0.18.20 - '@esbuild/linux-ia32': 0.18.20 - '@esbuild/linux-loong64': 0.18.20 - '@esbuild/linux-mips64el': 0.18.20 - '@esbuild/linux-ppc64': 0.18.20 - '@esbuild/linux-riscv64': 0.18.20 - '@esbuild/linux-s390x': 0.18.20 - '@esbuild/linux-x64': 0.18.20 - '@esbuild/netbsd-x64': 0.18.20 - '@esbuild/openbsd-x64': 0.18.20 - '@esbuild/sunos-x64': 0.18.20 - '@esbuild/win32-arm64': 0.18.20 - '@esbuild/win32-ia32': 0.18.20 - '@esbuild/win32-x64': 0.18.20 - dev: true - /escalade@3.1.1: resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==} engines: {node: '>=6'} @@ -6872,14 +6133,6 @@ packages: dependencies: eslint: 8.53.0 - /eslint-plugin-react-refresh@0.4.9(eslint@8.53.0): - resolution: {integrity: sha512-QK49YrBAo5CLNLseZ7sZgvgTy21E6NEw22eZqc4teZfH8pxV3yXc9XXOYfUI6JNpw7mfHNkAeWtBxrTyykB6HA==} - peerDependencies: - eslint: '>=7' - dependencies: - eslint: 8.53.0 - dev: true - /eslint-plugin-react@7.33.2(eslint@8.53.0): resolution: {integrity: sha512-73QQMKALArI8/7xGLNI/3LylrEYrlKZSb5C9+q3OtOewTnMQi5cT+aE9E41sLCmli3I9PGGmD1yiZydyo4FEPw==} engines: {node: '>=4'} @@ -7056,10 +6309,6 @@ packages: /estree-walker@1.0.1: resolution: {integrity: sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==} - /estree-walker@2.0.2: - resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} - dev: true - /esutils@2.0.3: resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} engines: {node: '>=0.10.0'} @@ -7378,15 +6627,6 @@ packages: jsonfile: 6.1.0 universalify: 2.0.0 - /fs-extra@7.0.1: - resolution: {integrity: sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==} - engines: {node: '>=6 <7 || >=8'} - dependencies: - graceful-fs: 4.2.11 - jsonfile: 4.0.0 - universalify: 0.1.2 - dev: true - /fs-extra@9.1.0: resolution: {integrity: sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==} engines: {node: '>=10'} @@ -7853,11 +7093,6 @@ packages: parent-module: 1.0.1 resolve-from: 4.0.0 - /import-lazy@4.0.0: - resolution: {integrity: sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==} - engines: {node: '>=8'} - dev: true - /import-local@3.1.0: resolution: {integrity: sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==} engines: {node: '>=8'} @@ -8860,12 +8095,6 @@ packages: engines: {node: '>=6'} hasBin: true - /jsonfile@4.0.0: - resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==} - optionalDependencies: - graceful-fs: 4.2.11 - dev: true - /jsonfile@6.1.0: resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} dependencies: @@ -8918,10 +8147,6 @@ packages: resolution: {integrity: sha512-pJiBpiXMbt7dkzXe8Ghj/u4FfXOOa98fPW+bihOJ4SjnoijweJrNThJfd3ifXpXhREjpoF2mZVH1GfS9LV3kHQ==} engines: {node: '>= 8'} - /kolorist@1.8.0: - resolution: {integrity: sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==} - dev: true - /language-subtag-registry@0.3.22: resolution: {integrity: sha512-tN0MCzyWnoz/4nHS6uxdlFWoUZT7ABptwKPQ52Ea7URk6vll88bWBVhodtnlfEuCcKWNGoc+uGbw1cwa9IKh/w==} @@ -9040,14 +8265,6 @@ packages: resolution: {integrity: sha512-ff3BX/tSioo+XojX4MOsOMhJw0nZoUEF011LX8g8d3gvjVbxd89cCio4BCXronjxcTUIJUoqKEUA+n4CqvvRPw==} dev: true - /lodash.get@4.4.2: - resolution: {integrity: sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==} - dev: true - - /lodash.isequal@4.5.0: - resolution: {integrity: sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==} - dev: true - /lodash.memoize@4.1.2: resolution: {integrity: sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==} @@ -9101,12 +8318,6 @@ packages: dependencies: sourcemap-codec: 1.4.8 - /magic-string@0.30.11: - resolution: {integrity: sha512-+Wri9p0QHMy+545hKww7YAu5NyzF8iomPL/RQazugQ9+Ez4Ic3mERMd8ZTX5rfK944j+560ZJi8iAwgak1Ac7A==} - dependencies: - '@jridgewell/sourcemap-codec': 1.5.0 - dev: true - /make-dir@3.1.0: resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==} engines: {node: '>=8'} @@ -9248,13 +8459,6 @@ packages: dependencies: brace-expansion: 2.0.1 - /minimatch@9.0.5: - resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} - engines: {node: '>=16 || 14 >=14.17'} - dependencies: - brace-expansion: 2.0.1 - dev: true - /minimist-options@4.1.0: resolution: {integrity: sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==} engines: {node: '>= 6'} @@ -9282,10 +8486,6 @@ packages: /ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} - /muggle-string@0.3.1: - resolution: {integrity: sha512-ckmWDJjphvd/FvZawgygcUeQCxzvohjFO5RxTjj4eq8kw359gFF3E1brjfI+viLMxss5JrHTDRHZvu2/tuy0Qg==} - dev: true - /multicast-dns@7.2.5: resolution: {integrity: sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg==} hasBin: true @@ -9298,12 +8498,6 @@ packages: engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true - /nanoid@3.3.7: - resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} - engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} - hasBin: true - dev: true - /natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} @@ -9644,10 +8838,6 @@ packages: no-case: 3.0.4 tslib: 2.6.2 - /path-browserify@1.0.1: - resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} - dev: true - /path-exists@3.0.0: resolution: {integrity: sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==} engines: {node: '>=4'} @@ -10429,15 +9619,6 @@ packages: picocolors: 1.0.0 source-map-js: 1.0.2 - /postcss@8.4.40: - resolution: {integrity: sha512-YF2kKIUzAofPMpfH6hOi2cGnv/HrUlfucspc7pDyvv7kGdqXrfj8SCl/t8owkEgKEuu8ZcRjSOxFxVLqwChZ2Q==} - engines: {node: ^10 || ^12 || >=14} - dependencies: - nanoid: 3.3.7 - picocolors: 1.0.1 - source-map-js: 1.2.0 - dev: true - /prelude-ls@1.1.2: resolution: {integrity: sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==} engines: {node: '>= 0.8.0'} @@ -11144,13 +10325,6 @@ packages: resolution: {integrity: sha512-J1l+Zxxp4XK3LUDZ9m60LRJF/mAe4z6a4xyabPHk7pvK5t35dACV32iIjJDFeWZFfZlO29w6SZ67knR0tHzJtQ==} engines: {node: '>=10'} - /resolve@1.19.0: - resolution: {integrity: sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==} - dependencies: - is-core-module: 2.10.0 - path-parse: 1.0.7 - dev: true - /resolve@1.22.1: resolution: {integrity: sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==} hasBin: true @@ -11218,14 +10392,6 @@ packages: optionalDependencies: fsevents: 2.3.3 - /rollup@3.29.4: - resolution: {integrity: sha512-oWzmBZwvYrU0iJHtDmhsm662rC15FRXmcjCk1xD771dFDx5jJ02ufAQQTn0etB2emNk4J9EZg/yWKpsn9BWGRw==} - engines: {node: '>=14.18.0', npm: '>=8.0.0'} - hasBin: true - optionalDependencies: - fsevents: 2.3.3 - dev: true - /run-applescript@5.0.0: resolution: {integrity: sha512-XcT5rBksx1QdIhlFOCtgZkB99ZEouFZ1E2Kc2LHqNW13U3/74YGdkQRmThTwxy4QIyookibDKYZOPqX//6BlAg==} engines: {node: '>=12'} @@ -11568,11 +10734,6 @@ packages: resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==} engines: {node: '>=0.10.0'} - /source-map-js@1.2.0: - resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==} - engines: {node: '>=0.10.0'} - dev: true - /source-map-loader@3.0.1(webpack@5.74.0): resolution: {integrity: sha512-Vp1UsfyPvgujKQzi4pyDiTOnE3E4H+yHvkVRN3c/9PJmQS4CQJExvcDvaX/D+RV+xQben9HJ56jMJS3CgUeWyA==} engines: {node: '>= 12.13.0'} @@ -12161,11 +11322,6 @@ packages: resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} engines: {node: '>=0.6'} - /tosource@2.0.0-alpha.3: - resolution: {integrity: sha512-KAB2lrSS48y91MzFPFuDg4hLbvDiyTjOVgaK7Erw+5AmZXNq4sFRVn8r6yxSLuNs15PaokrDRpS61ERY9uZOug==} - engines: {node: '>=10'} - dev: true - /tough-cookie@4.1.2: resolution: {integrity: sha512-G9fqXWoYFZgTc2z8Q5zaHy/vJMjm+WV0AkAeHxVCQiEB1b+dGvWzFW6QV07cY5jQ5gRkeid2qIkzkxUnmoQZUQ==} engines: {node: '>=6'} @@ -12203,15 +11359,6 @@ packages: typescript: 4.9.5 dev: true - /ts-api-utils@1.0.3(typescript@5.5.4): - resolution: {integrity: sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg==} - engines: {node: '>=16.13.0'} - peerDependencies: - typescript: '>=4.2.0' - dependencies: - typescript: 5.5.4 - dev: true - /ts-node@10.9.1(@types/node@16.11.59)(typescript@4.9.5): resolution: {integrity: sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==} hasBin: true @@ -12364,18 +11511,6 @@ packages: engines: {node: '>=4.2.0'} hasBin: true - /typescript@5.4.2: - resolution: {integrity: sha512-+2/g0Fds1ERlP6JsakQQDXjZdZMM+rqpamFZJEKh4kwTIn3iDkgKtby0CeNd5ATNZ4Ry1ax15TMx0W2V+miizQ==} - engines: {node: '>=14.17'} - hasBin: true - dev: true - - /typescript@5.5.4: - resolution: {integrity: sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==} - engines: {node: '>=14.17'} - hasBin: true - dev: true - /unbox-primitive@1.0.2: resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==} dependencies: @@ -12429,11 +11564,6 @@ packages: dependencies: crypto-random-string: 2.0.0 - /universalify@0.1.2: - resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} - engines: {node: '>= 4.0.0'} - dev: true - /universalify@0.2.0: resolution: {integrity: sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==} engines: {node: '>= 4.0.0'} @@ -12543,101 +11673,15 @@ packages: spdx-expression-parse: 3.0.1 dev: true - /validator@13.12.0: - resolution: {integrity: sha512-c1Q0mCiPlgdTVVVIJIrBuxNicYE+t/7oKeI9MWLj3fh/uq2Pxh/3eeWbVZ4OcGW1TUf53At0njHw5SMdA3tmMg==} - engines: {node: '>= 0.10'} - dev: true - /vary@1.1.2: resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} engines: {node: '>= 0.8'} - /vite-plugin-dts@3.9.1(@types/node@16.11.59)(rollup@2.79.0)(typescript@5.5.4)(vite@4.5.3): - resolution: {integrity: sha512-rVp2KM9Ue22NGWB8dNtWEr+KekN3rIgz1tWD050QnRGlriUCmaDwa7qA5zDEjbXg5lAXhYMSBJtx3q3hQIJZSg==} - engines: {node: ^14.18.0 || >=16.0.0} - peerDependencies: - typescript: '*' - vite: '*' - peerDependenciesMeta: - vite: - optional: true - dependencies: - '@microsoft/api-extractor': 7.43.0(@types/node@16.11.59) - '@rollup/pluginutils': 5.1.0(rollup@2.79.0) - '@vue/language-core': 1.8.27(typescript@5.5.4) - debug: 4.3.4 - kolorist: 1.8.0 - magic-string: 0.30.11 - typescript: 5.5.4 - vite: 4.5.3(@types/node@16.11.59)(sass@1.54.9) - vue-tsc: 1.8.27(typescript@5.5.4) - transitivePeerDependencies: - - '@types/node' - - rollup - - supports-color - dev: true - - /vite@4.5.3(@types/node@16.11.59)(sass@1.54.9): - resolution: {integrity: sha512-kQL23kMeX92v3ph7IauVkXkikdDRsYMGTVl5KY2E9OY4ONLvkHf04MDTbnfo6NKxZiDLWzVpP5oTa8hQD8U3dg==} - engines: {node: ^14.18.0 || >=16.0.0} - hasBin: true - peerDependencies: - '@types/node': '>= 14' - less: '*' - lightningcss: ^1.21.0 - sass: '*' - stylus: '*' - sugarss: '*' - terser: ^5.4.0 - peerDependenciesMeta: - '@types/node': - optional: true - less: - optional: true - lightningcss: - optional: true - sass: - optional: true - stylus: - optional: true - sugarss: - optional: true - terser: - optional: true - dependencies: - '@types/node': 16.11.59 - esbuild: 0.18.20 - postcss: 8.4.40 - rollup: 3.29.4 - sass: 1.54.9 - optionalDependencies: - fsevents: 2.3.3 - dev: true - /void-elements@3.1.0: resolution: {integrity: sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==} engines: {node: '>=0.10.0'} dev: false - /vue-template-compiler@2.7.16: - resolution: {integrity: sha512-AYbUWAJHLGGQM7+cNTELw+KsOG9nl2CnSv467WobS5Cv9uk3wFcnr1Etsz2sEIHEZvw1U+o9mRlEO6QbZvUPGQ==} - dependencies: - de-indent: 1.0.2 - he: 1.2.0 - dev: true - - /vue-tsc@1.8.27(typescript@5.5.4): - resolution: {integrity: sha512-WesKCAZCRAbmmhuGl3+VrdWItEvfoFIPXOvUJkjULi+x+6G/Dy69yO3TBRJDr9eUlmsNAwVmxsNZxvHKzbkKdg==} - hasBin: true - peerDependencies: - typescript: '*' - dependencies: - '@volar/typescript': 1.11.1 - '@vue/language-core': 1.8.27(typescript@5.5.4) - semver: 7.5.4 - typescript: 5.5.4 - dev: true - /w3c-hr-time@1.0.2: resolution: {integrity: sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==} dependencies: @@ -13274,18 +12318,6 @@ packages: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} - /z-schema@5.0.5: - resolution: {integrity: sha512-D7eujBWkLa3p2sIpJA0d1pr7es+a7m0vFAnZLlCEKq/Ij2k0MLi9Br2UPxoxdYystm5K1yeBGzub0FlYUEWj2Q==} - engines: {node: '>=8.0.0'} - hasBin: true - dependencies: - lodash.get: 4.4.2 - lodash.isequal: 4.5.0 - validator: 13.12.0 - optionalDependencies: - commander: 9.5.0 - dev: true - /zustand@4.1.1(react@18.2.0): resolution: {integrity: sha512-h4F3WMqsZgvvaE0n3lThx4MM81Ls9xebjvrABNzf5+jb3/03YjNTSgZXeyrvXDArMeV9untvWXRw1tY+ntPYbA==} engines: {node: '>=12.7.0'} diff --git a/ui/src/common/interface.ts b/ui/src/common/interface.ts index 41dc0fef0..789109d2f 100644 --- a/ui/src/common/interface.ts +++ b/ui/src/common/interface.ts @@ -735,3 +735,35 @@ export interface ReactionItem { tooltip: string; is_active: boolean; } + +export interface BadgeListItem { + id: string; + name: string; + icon: string; + award_count: number; + earned: boolean; + /** 1: bronze 2: silver 3:gold */ + level: number; +} + +export interface BadgeListGroupItem { + badges: BadgeListItem[]; + group_name: string; +} + +export interface BadgeInfo extends BadgeListItem { + description: string; + earned_count: number; + is_single: boolean; +} + +export interface BadgeDetailListReq { + page: number; + page_size: number; + badge_id: string; +} + +export interface BadgeDetailListRes { + count: number; + list: BadgeInfo[]; +} diff --git a/ui/src/components/CardBadge/index.tsx b/ui/src/components/CardBadge/index.tsx index 9251b38ce..6fb4af7c0 100644 --- a/ui/src/components/CardBadge/index.tsx +++ b/ui/src/components/CardBadge/index.tsx @@ -19,35 +19,59 @@ import { useTranslation } from 'react-i18next'; import { FC } from 'react'; import { Card, Badge } from 'react-bootstrap'; +import { Link } from 'react-router-dom'; +import classnames from 'classnames'; + +import { Icon } from '@/components'; +import * as Type from '@/common/interface'; import { formatCount } from '@/utils'; import './index.scss'; interface IProps { - data: any; - badgePill: boolean; + data: Type.BadgeListItem; showAwardedCount?: boolean; } -const Index: FC = ({ data, badgePill, showAwardedCount = false }) => { +const Index: FC = ({ data, showAwardedCount = false }) => { const { t } = useTranslation('translation', { keyPrefix: 'badges' }); console.log(data); return ( - + - - 0 - - -
Nice Question
+ {data.earned && ( + + {t('earned')} + + )} + + {/* + {showEarned ? t('earned') : data.award_count} + */} + {data.icon.startsWith('http') ? ( + {data.name} + ) : ( + + )} + +
{data.name}
{showAwardedCount && (
- {t('x_awarded', { number: formatCount(16) })} + {t('x_awarded', { number: formatCount(data.award_count) })}
)}
-
+ ); }; diff --git a/ui/src/index.scss b/ui/src/index.scss index b174fbc58..6d3ade051 100644 --- a/ui/src/index.scss +++ b/ui/src/index.scss @@ -352,3 +352,13 @@ img[src=""] { .alert-exist { color: var(--an-alert-exist-color); } + +.bronze { + color: #CD7F32; +} +.silver { + color: #C0C0C0; +} +.gold { + color: #FFD700; +} diff --git a/ui/src/pages/Badges/Detail/components/Badge/index.tsx b/ui/src/pages/Badges/Detail/components/Badge/index.tsx new file mode 100644 index 000000000..1fc5d43d3 --- /dev/null +++ b/ui/src/pages/Badges/Detail/components/Badge/index.tsx @@ -0,0 +1,93 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { FC } from 'react'; +import { Card, Badge } from 'react-bootstrap'; +import { useTranslation } from 'react-i18next'; + +import classnames from 'classnames'; + +import * as Type from '@/common/interface'; +import { Icon } from '@/components'; +import { formatCount } from '@/utils'; + +interface IProps { + data: Type.BadgeInfo; +} + +const Index: FC = ({ data }) => { + const { t } = useTranslation('translation', { keyPrefix: 'badges' }); + + if (!data?.id) { + return null; + } + + return ( + + + {data.icon?.startsWith('http') ? ( + {data.name} + ) : ( + + )} +
+
{data.name}
+
+ + {!data.is_single && ( +
{t('can_earn_multiple')}
+ )} + + {data.award_count > 0 && data.earned_count > 0 && ( +
+ {data.award_count > 0 && ( + + {t('x_awarded', { number: formatCount(data.award_count) })} + + )} + + {data.earned_count > 0 && ( + + {t('earned_x', { number: data.earned_count })} + + )} +
+ )} +
+ + + ); +}; + +export default Index; diff --git a/ui/src/pages/Badges/Detail/components/HeaderLoader/index.tsx b/ui/src/pages/Badges/Detail/components/HeaderLoader/index.tsx new file mode 100644 index 000000000..df0c92048 --- /dev/null +++ b/ui/src/pages/Badges/Detail/components/HeaderLoader/index.tsx @@ -0,0 +1,49 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { Card } from 'react-bootstrap'; + +const Index = () => { + return ( + + +
+ +
+
+
+
+ +
+ +
+ + + +
+
+ + + ); +}; + +export default Index; diff --git a/ui/src/pages/Badges/Detail/components/Loader/index.tsx b/ui/src/pages/Badges/Detail/components/Loader/index.tsx new file mode 100644 index 000000000..515e69af1 --- /dev/null +++ b/ui/src/pages/Badges/Detail/components/Loader/index.tsx @@ -0,0 +1,55 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { FC, memo } from 'react'; +import { Col } from 'react-bootstrap'; + +interface Props { + count?: number; +} + +const Index: FC = ({ count = 12 }) => { + const list = new Array(count).fill(0).map((v, i) => v + i); + return ( + <> + {list.map((v) => ( + +
+
+
+
+
+
+
+
+
+
+ + ))} + + ); +}; + +export default memo(Index); diff --git a/ui/src/pages/Badges/Detail/index.tsx b/ui/src/pages/Badges/Detail/index.tsx index 7a192a13a..3e8513ae6 100644 --- a/ui/src/pages/Badges/Detail/index.tsx +++ b/ui/src/pages/Badges/Detail/index.tsx @@ -17,79 +17,98 @@ * under the License. */ -import { Card, Badge, Row, Col } from 'react-bootstrap'; +import { Row, Col } from 'react-bootstrap'; import { useTranslation } from 'react-i18next'; -import { Link } from 'react-router-dom'; +import { Link, useParams, useSearchParams } from 'react-router-dom'; -import { Avatar, FormatTime } from '@/components'; -import { usePageTags } from '@/hooks'; -import { formatCount } from '@/utils'; +// import classnames from 'classnames'; + +import { Avatar, FormatTime, Pagination, Empty } from '@/components'; +import { usePageTags, useSkeletonControl } from '@/hooks'; +// import { formatCount } from '@/utils'; +import { useGetBadgeInfo, useBadgeDetailList } from '@/services'; + +import BadgeDetail from './components/Badge'; +import Loader from './components/Loader'; +import HeaderLoader from './components/HeaderLoader'; const Index = () => { const { t } = useTranslation('translation', { keyPrefix: 'badges' }); + const { badge_id = '' } = useParams(); + const [urlSearchParams] = useSearchParams(); + + const page = Number(urlSearchParams.get('page')) || 1; + const pageSize = 30; + const { data: badgeInfo, isLoading: isHeaderLoading } = + useGetBadgeInfo(badge_id); + const { data: badges, isLoading: isDetailLoading } = useBadgeDetailList({ + badge_id, + page, + page_size: pageSize, + }); + + const { isSkeletonShow } = useSkeletonControl(isDetailLoading); + usePageTags({ - title: t('title'), + title: badgeInfo?.name || '', }); + if (badgeInfo === undefined) { + return null; + } + + console.log(badges); + return (

{t('title')}

- - - -
-
Support Expert
-
- This badge is granted for achieving the expert level of our - community support programme. This certifies that the recipient has - demonstrated a high level of skills and abilities to manage and - support multiple communities/instances. -
-
{t('can_earn_multiple')}
-
- - {t('x_awarded', { number: formatCount(16) })} - - - {t('earned_x', { number: 2 })} - -
-
-
-
+ {isHeaderLoading ? : } - {[0, 1, 2, 3, 4, 5, 6].map((item) => { - return ( - - -
- - - -
- - username + + {isSkeletonShow ? ( + + ) : ( + badges?.list?.map((item) => { + return ( + + +
+ + -
- 980 {t('x_reputation', { keyPrefix: 'personal' })} +
+ + username + +
+ 980 {t('x_reputation', { keyPrefix: 'personal' })} +
-
- - How to `go test` all tests in my project? - - - ); - })} + + How to `go test` all tests in my project? + + + ); + }) + )} + {Number(badges?.count) <= 0 && !isDetailLoading && } +
+ +
); }; diff --git a/ui/src/pages/Badges/index.tsx b/ui/src/pages/Badges/index.tsx index 84784c53e..396280620 100644 --- a/ui/src/pages/Badges/index.tsx +++ b/ui/src/pages/Badges/index.tsx @@ -21,10 +21,13 @@ import { useTranslation } from 'react-i18next'; import { CardBadge } from '@/components'; import { usePageTags } from '@/hooks'; +import { useGetAllBadges } from '@/services'; const Index = () => { const { t } = useTranslation('translation', { keyPrefix: 'badges' }); + const { data: badgesList } = useGetAllBadges(); + usePageTags({ title: t('title'), }); @@ -32,12 +35,18 @@ const Index = () => { return (

{t('title')}

-
Community Badges
-
- {[0, 1, 2, 3, 4, 5, 6].map((item) => { - return ; - })} -
+ {badgesList?.map((item) => { + return ( +
+
{item.group_name}
+
+ {item.badges?.map((badge) => { + return ; + })} +
+
+ ); + })}
); }; diff --git a/ui/src/pages/Users/Personal/components/Badges/index.tsx b/ui/src/pages/Users/Personal/components/Badges/index.tsx index a7bd3e58a..0ef00f39a 100644 --- a/ui/src/pages/Users/Personal/components/Badges/index.tsx +++ b/ui/src/pages/Users/Personal/components/Badges/index.tsx @@ -19,10 +19,11 @@ import { FC } from 'react'; +import * as Type from '@/common/interface'; import { CardBadge } from '@/components'; interface IProps { - data: any[]; + data: Type.BadgeListItem[]; visible: boolean; } @@ -33,8 +34,8 @@ const Index: FC = ({ data, visible }) => { } return (
- {[0, 1, 2, 3, 4, 5, 6].map((item) => { - return ; + {data.map((item) => { + return ; })}
); diff --git a/ui/src/pages/Users/Personal/components/Overview/index.tsx b/ui/src/pages/Users/Personal/components/Overview/index.tsx index 384689dcb..b4dd548b7 100644 --- a/ui/src/pages/Users/Personal/components/Overview/index.tsx +++ b/ui/src/pages/Users/Personal/components/Overview/index.tsx @@ -21,7 +21,8 @@ import { FC, memo } from 'react'; import { useTranslation } from 'react-i18next'; import { Row, Col } from 'react-bootstrap'; -import { CardBadge } from '@/components'; +// import * as Type from '@/common/interface'; +// import { CardBadge } from '@/components'; import TopList from '../TopList'; interface Props { @@ -67,11 +68,11 @@ const Index: FC = ({ visible, introduction, data }) => {
{t('recent_badges')}
-
+ {/*
{[0, 1, 2, 3, 4, 5, 6].map((item) => { return ; })} -
+
*/}
); diff --git a/ui/src/services/client/badges.ts b/ui/src/services/client/badges.ts new file mode 100644 index 000000000..c3a7bba23 --- /dev/null +++ b/ui/src/services/client/badges.ts @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import useSWR from 'swr'; +import qs from 'qs'; + +import request from '@/utils/request'; +import type * as Type from '@/common/interface'; + +export const useGetAllBadges = () => { + const apiUrl = '/answer/api/v1/badges'; + const { data, error, mutate } = useSWR, Error>( + apiUrl, + request.instance.get, + ); + return { + data, + isLoading: !data && !error, + error, + mutate, + }; +}; + +export const useGetBadgeInfo = (id: string) => { + const { data, error, mutate } = useSWR( + `/answer/api/v1/badge?id=${id}`, + request.instance.get, + ); + return { + data, + isLoading: !data && !error, + error, + mutate, + }; +}; + +export const useBadgeDetailList = (params: Type.BadgeDetailListReq) => { + const { data, error, mutate } = useSWR( + `/answer/api/v1/badge/awards/page?${qs.stringify(params)}`, + request.instance.get, + ); + return { + data, + isLoading: !data && !error, + error, + mutate, + }; +}; diff --git a/ui/src/services/client/index.ts b/ui/src/services/client/index.ts index 0d7184237..005bc26d7 100644 --- a/ui/src/services/client/index.ts +++ b/ui/src/services/client/index.ts @@ -30,3 +30,4 @@ export * from './revision'; export * from './user'; export * from './Oauth'; export * from './review'; +export * from './badges'; From fce712e6c7a7d907c2d576725fa46b07504a5e01 Mon Sep 17 00:00:00 2001 From: shuai Date: Mon, 12 Aug 2024 16:26:31 +0800 Subject: [PATCH 23/64] fix: bages detail add userCard conponent --- ui/src/common/interface.ts | 14 ++- .../Detail/components/HeaderLoader/index.tsx | 16 +-- .../Detail/components/UserCard/index.tsx | 97 +++++++++++++++++++ ui/src/pages/Badges/Detail/index.tsx | 28 ++---- ui/src/pages/Badges/index.tsx | 1 - ui/src/services/client/badges.ts | 4 +- ui/src/services/client/personal.ts | 8 +- 7 files changed, 134 insertions(+), 34 deletions(-) create mode 100644 ui/src/pages/Badges/Detail/components/UserCard/index.tsx diff --git a/ui/src/common/interface.ts b/ui/src/common/interface.ts index 789109d2f..45f957c2f 100644 --- a/ui/src/common/interface.ts +++ b/ui/src/common/interface.ts @@ -761,9 +761,21 @@ export interface BadgeDetailListReq { page: number; page_size: number; badge_id: string; + username?: string | null; +} + +export interface BadgeDetailListItem { + created_at: number; + author_user_info: UserInfoBase; + object_type: string; + object_id: string; + url_title: string; + question_id: string; + answer_id: string; + comment_id: string; } export interface BadgeDetailListRes { count: number; - list: BadgeInfo[]; + list: BadgeDetailListItem[]; } diff --git a/ui/src/pages/Badges/Detail/components/HeaderLoader/index.tsx b/ui/src/pages/Badges/Detail/components/HeaderLoader/index.tsx index df0c92048..d56432e81 100644 --- a/ui/src/pages/Badges/Detail/components/HeaderLoader/index.tsx +++ b/ui/src/pages/Badges/Detail/components/HeaderLoader/index.tsx @@ -22,23 +22,23 @@ import { Card } from 'react-bootstrap'; const Index = () => { return ( - +
-
-
-
-
+
+
+
+
- + - +
diff --git a/ui/src/pages/Badges/Detail/components/UserCard/index.tsx b/ui/src/pages/Badges/Detail/components/UserCard/index.tsx new file mode 100644 index 000000000..d874ceb9e --- /dev/null +++ b/ui/src/pages/Badges/Detail/components/UserCard/index.tsx @@ -0,0 +1,97 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { memo, FC } from 'react'; +import { Link } from 'react-router-dom'; +import { useTranslation } from 'react-i18next'; + +import classnames from 'classnames'; + +import { Avatar } from '@/components'; +import { formatCount } from '@/utils'; + +interface Props { + data: any; + className?: string; +} + +const Index: FC = ({ data, className = '' }) => { + const { t } = useTranslation('translation', { keyPrefix: 'badges' }); + return ( +
+ {data?.status !== 'deleted' ? ( + + + + + + ) : ( + <> + + + + + )} +
+
+ {data?.status !== 'deleted' ? ( + + {data?.display_name} + + ) : ( + {data?.display_name} + )} +
+
+ {formatCount(data?.rank)}{' '} + {t('x_reputation', { keyPrefix: 'personal' })} +
+
+
+ ); +}; + +export default memo(Index); diff --git a/ui/src/pages/Badges/Detail/index.tsx b/ui/src/pages/Badges/Detail/index.tsx index 3e8513ae6..42487db30 100644 --- a/ui/src/pages/Badges/Detail/index.tsx +++ b/ui/src/pages/Badges/Detail/index.tsx @@ -23,7 +23,7 @@ import { Link, useParams, useSearchParams } from 'react-router-dom'; // import classnames from 'classnames'; -import { Avatar, FormatTime, Pagination, Empty } from '@/components'; +import { FormatTime, Pagination, Empty } from '@/components'; import { usePageTags, useSkeletonControl } from '@/hooks'; // import { formatCount } from '@/utils'; import { useGetBadgeInfo, useBadgeDetailList } from '@/services'; @@ -31,6 +31,7 @@ import { useGetBadgeInfo, useBadgeDetailList } from '@/services'; import BadgeDetail from './components/Badge'; import Loader from './components/Loader'; import HeaderLoader from './components/HeaderLoader'; +import UserCard from './components/UserCard'; const Index = () => { const { t } = useTranslation('translation', { keyPrefix: 'badges' }); @@ -46,6 +47,7 @@ const Index = () => { badge_id, page, page_size: pageSize, + username: urlSearchParams.get('username') || null, }); const { isSkeletonShow } = useSkeletonControl(isDetailLoading); @@ -65,36 +67,20 @@ const Index = () => {

{t('title')}

{isHeaderLoading ? : } - {isSkeletonShow ? ( ) : ( badges?.list?.map((item) => { return ( - + -
- - - -
- - username - -
- 980 {t('x_reputation', { keyPrefix: 'personal' })} -
-
-
+ - How to `go test` all tests in my project? + {item.url_title} ); diff --git a/ui/src/pages/Badges/index.tsx b/ui/src/pages/Badges/index.tsx index 396280620..d37f5d9a8 100644 --- a/ui/src/pages/Badges/index.tsx +++ b/ui/src/pages/Badges/index.tsx @@ -16,7 +16,6 @@ * specific language governing permissions and limitations * under the License. */ - import { useTranslation } from 'react-i18next'; import { CardBadge } from '@/components'; diff --git a/ui/src/services/client/badges.ts b/ui/src/services/client/badges.ts index c3a7bba23..476f85d12 100644 --- a/ui/src/services/client/badges.ts +++ b/ui/src/services/client/badges.ts @@ -52,7 +52,9 @@ export const useGetBadgeInfo = (id: string) => { export const useBadgeDetailList = (params: Type.BadgeDetailListReq) => { const { data, error, mutate } = useSWR( - `/answer/api/v1/badge/awards/page?${qs.stringify(params)}`, + `/answer/api/v1/badge/awards/page?${qs.stringify(params, { + skipNulls: true, + })}`, request.instance.get, ); return { diff --git a/ui/src/services/client/personal.ts b/ui/src/services/client/personal.ts index 339f1e605..fbf389389 100644 --- a/ui/src/services/client/personal.ts +++ b/ui/src/services/client/personal.ts @@ -66,7 +66,7 @@ export const usePersonalTop = (username: string, tabName: string) => { }; export const usePersonalListByTabName = (params: ListReq, tabName: string) => { - let apiUrl = ''; + let apiUrl: string | null = ''; if (tabName === 'answers') { apiUrl = '/answer/api/v1/personal/answer/page'; } @@ -89,10 +89,14 @@ export const usePersonalListByTabName = (params: ListReq, tabName: string) => { delete params.username; apiUrl = '/answer/api/v1/personal/vote/page'; } + if (tabName === 'badges') { + delete params.order; + apiUrl = '/answer/api/v1/badge/user/awards'; + } const queryParams = qs.stringify(params, { skipNulls: true }); const { data, error, mutate } = useSWR( - tabName !== 'overview' ? `${apiUrl}?${queryParams}` : null, + tabName !== 'review' ? `${apiUrl}?${queryParams}` : null, request.instance.get, ); From 3d71bc17c843a78b71f30a94eed55600a1d3f89d Mon Sep 17 00:00:00 2001 From: shuai Date: Mon, 12 Aug 2024 17:45:03 +0800 Subject: [PATCH 24/64] fix: badge detail list styles format --- i18n/en_US.yaml | 2 +- ui/src/common/interface.ts | 1 + ui/src/components/CardBadge/index.tsx | 23 +++++++++---- .../Badges/Detail/components/Badge/index.tsx | 6 ++-- .../Detail/components/UserCard/index.tsx | 31 ++++++++--------- .../Personal/components/Badges/index.tsx | 11 +++++-- .../Personal/components/Overview/index.tsx | 33 +++++++++++++------ ui/src/pages/Users/Personal/index.tsx | 7 +++- ui/src/services/client/badges.ts | 16 +++++++++ 9 files changed, 89 insertions(+), 41 deletions(-) diff --git a/i18n/en_US.yaml b/i18n/en_US.yaml index 09596b783..420d25bac 100644 --- a/i18n/en_US.yaml +++ b/i18n/en_US.yaml @@ -1743,7 +1743,7 @@ ui: earned_x: Earned x{{ number }} x_awarded: "{{ number }} awarded" can_earn_multiple: You can earn this multiple times. - + earned: Earned admin: admin_header: diff --git a/ui/src/common/interface.ts b/ui/src/common/interface.ts index 45f957c2f..608a2465e 100644 --- a/ui/src/common/interface.ts +++ b/ui/src/common/interface.ts @@ -744,6 +744,7 @@ export interface BadgeListItem { earned: boolean; /** 1: bronze 2: silver 3:gold */ level: number; + earned_count?: number; } export interface BadgeListGroupItem { diff --git a/ui/src/components/CardBadge/index.tsx b/ui/src/components/CardBadge/index.tsx index 6fb4af7c0..bb42630c6 100644 --- a/ui/src/components/CardBadge/index.tsx +++ b/ui/src/components/CardBadge/index.tsx @@ -32,23 +32,34 @@ import './index.scss'; interface IProps { data: Type.BadgeListItem; showAwardedCount?: boolean; + urlSearchParams?: string; + badgePillType?: 'earned' | 'count'; } -const Index: FC = ({ data, showAwardedCount = false }) => { +const Index: FC = ({ + data, + badgePillType = 'earned', + showAwardedCount = false, + urlSearchParams, +}) => { const { t } = useTranslation('translation', { keyPrefix: 'badges' }); console.log(data); return ( - + - {data.earned && ( + {data.earned && badgePillType === 'earned' && ( {t('earned')} )} - {/* - {showEarned ? t('earned') : data.award_count} - */} + {badgePillType === 'count' && Number(data?.earned_count) > 0 && ( + + x{data.earned_count} + + )} {data.icon.startsWith('http') ? ( {data.name} ) : ( diff --git a/ui/src/pages/Badges/Detail/components/Badge/index.tsx b/ui/src/pages/Badges/Detail/components/Badge/index.tsx index 1fc5d43d3..affb73e6e 100644 --- a/ui/src/pages/Badges/Detail/components/Badge/index.tsx +++ b/ui/src/pages/Badges/Detail/components/Badge/index.tsx @@ -69,16 +69,16 @@ const Index: FC = ({ data }) => {
{t('can_earn_multiple')}
)} - {data.award_count > 0 && data.earned_count > 0 && ( + {(data.award_count > 0 || data.earned_count > 0) && (
{data.award_count > 0 && ( - + {t('x_awarded', { number: formatCount(data.award_count) })} )} {data.earned_count > 0 && ( - + {t('earned_x', { number: data.earned_count })} )} diff --git a/ui/src/pages/Badges/Detail/components/UserCard/index.tsx b/ui/src/pages/Badges/Detail/components/UserCard/index.tsx index d874ceb9e..116a81b33 100644 --- a/ui/src/pages/Badges/Detail/components/UserCard/index.tsx +++ b/ui/src/pages/Badges/Detail/components/UserCard/index.tsx @@ -21,20 +21,17 @@ import { memo, FC } from 'react'; import { Link } from 'react-router-dom'; import { useTranslation } from 'react-i18next'; -import classnames from 'classnames'; - import { Avatar } from '@/components'; import { formatCount } from '@/utils'; interface Props { data: any; - className?: string; } -const Index: FC = ({ data, className = '' }) => { +const Index: FC = ({ data }) => { const { t } = useTranslation('translation', { keyPrefix: 'badges' }); return ( -
+
{data?.status !== 'deleted' ? ( = ({ data, className = '' }) => { /> )} -
-
- {data?.status !== 'deleted' ? ( - - {data?.display_name} - - ) : ( - {data?.display_name} - )} -
+
+ {data?.status !== 'deleted' ? ( + + {data?.display_name} + + ) : ( + {data?.display_name} + )}
{formatCount(data?.rank)}{' '} {t('x_reputation', { keyPrefix: 'personal' })} diff --git a/ui/src/pages/Users/Personal/components/Badges/index.tsx b/ui/src/pages/Users/Personal/components/Badges/index.tsx index 0ef00f39a..cd6830c9a 100644 --- a/ui/src/pages/Users/Personal/components/Badges/index.tsx +++ b/ui/src/pages/Users/Personal/components/Badges/index.tsx @@ -24,10 +24,11 @@ import { CardBadge } from '@/components'; interface IProps { data: Type.BadgeListItem[]; + username: string; visible: boolean; } -const Index: FC = ({ data, visible }) => { +const Index: FC = ({ data, visible, username }) => { console.log(data); if (!visible) { return null; @@ -35,7 +36,13 @@ const Index: FC = ({ data, visible }) => { return (
{data.map((item) => { - return ; + return ( + + ); })}
); diff --git a/ui/src/pages/Users/Personal/components/Overview/index.tsx b/ui/src/pages/Users/Personal/components/Overview/index.tsx index b4dd548b7..6b247b0cc 100644 --- a/ui/src/pages/Users/Personal/components/Overview/index.tsx +++ b/ui/src/pages/Users/Personal/components/Overview/index.tsx @@ -22,16 +22,21 @@ import { useTranslation } from 'react-i18next'; import { Row, Col } from 'react-bootstrap'; // import * as Type from '@/common/interface'; -// import { CardBadge } from '@/components'; +import { CardBadge } from '@/components'; +import { useGetRecentAwardBadges } from '@/services'; import TopList from '../TopList'; interface Props { + username: string; visible: boolean; introduction: string; data; } -const Index: FC = ({ visible, introduction, data }) => { +const Index: FC = ({ visible, introduction, data, username }) => { const { t } = useTranslation('translation', { keyPrefix: 'personal' }); + const { data: recentBadges } = useGetRecentAwardBadges( + visible ? username : null, + ); if (!visible) { return null; } @@ -66,14 +71,22 @@ const Index: FC = ({ visible, introduction, data }) => { -
-
{t('recent_badges')}
- {/*
- {[0, 1, 2, 3, 4, 5, 6].map((item) => { - return ; - })} -
*/} -
+ {Number(recentBadges?.count) > 0 && ( +
+
{t('recent_badges')}
+
+ {recentBadges?.list?.map((item) => { + return ( + + ); + })} +
+
+ )}
); }; diff --git a/ui/src/pages/Users/Personal/index.tsx b/ui/src/pages/Users/Personal/index.tsx index 96704b858..e9516acee 100644 --- a/ui/src/pages/Users/Personal/index.tsx +++ b/ui/src/pages/Users/Personal/index.tsx @@ -102,6 +102,7 @@ const Personal: FC = () => { visible={tabName === 'overview'} introduction={userInfo?.bio_html || ''} data={topData} + username={username} /> { - + {!list?.length && !isLoading && } {count > 0 && ( diff --git a/ui/src/services/client/badges.ts b/ui/src/services/client/badges.ts index 476f85d12..7f3f8abd7 100644 --- a/ui/src/services/client/badges.ts +++ b/ui/src/services/client/badges.ts @@ -64,3 +64,19 @@ export const useBadgeDetailList = (params: Type.BadgeDetailListReq) => { mutate, }; }; + +export const useGetRecentAwardBadges = (username) => { + const apiUrl = username + ? `/answer/api/v1/badge/user/awards/recent?username=${username}` + : null; + const { data, error, mutate } = useSWR< + { count: number; list: Array }, + Error + >(apiUrl, request.instance.get); + return { + data, + isLoading: !data && !error, + error, + mutate, + }; +}; From 957c299fc5b4a7c7d69d3bbbf191683cafa149a1 Mon Sep 17 00:00:00 2001 From: shuai Date: Tue, 13 Aug 2024 10:03:03 +0800 Subject: [PATCH 25/64] fix: delete log --- ui/src/components/CardBadge/index.tsx | 1 - ui/src/pages/Users/Personal/components/Badges/index.tsx | 1 - 2 files changed, 2 deletions(-) diff --git a/ui/src/components/CardBadge/index.tsx b/ui/src/components/CardBadge/index.tsx index bb42630c6..0983f108f 100644 --- a/ui/src/components/CardBadge/index.tsx +++ b/ui/src/components/CardBadge/index.tsx @@ -43,7 +43,6 @@ const Index: FC = ({ urlSearchParams, }) => { const { t } = useTranslation('translation', { keyPrefix: 'badges' }); - console.log(data); return ( = ({ data, visible, username }) => { - console.log(data); if (!visible) { return null; } From 29f2ce3a5630d0426b9fb9be91201dbfa584a487 Mon Sep 17 00:00:00 2001 From: shuai Date: Wed, 14 Aug 2024 11:23:57 +0800 Subject: [PATCH 26/64] fix: add link url path --- ui/src/pages/Badges/Detail/index.tsx | 18 +++++++++++++++--- ui/src/utils/localize.ts | 4 +--- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/ui/src/pages/Badges/Detail/index.tsx b/ui/src/pages/Badges/Detail/index.tsx index 42487db30..a388d4963 100644 --- a/ui/src/pages/Badges/Detail/index.tsx +++ b/ui/src/pages/Badges/Detail/index.tsx @@ -71,6 +71,16 @@ const Index = () => { ) : ( badges?.list?.map((item) => { + const linkUrl = + item.object_type === 'question' + ? `/question/${item.question_id}` + : item.object_type === 'answer' + ? `/question/${item.question_id}/${item.answer_id}` + : item.object_type === 'comment' && item.answer_id + ? `/question/${item.question_id}/${item.answer_id}?commentId=${item.comment_id}` + : item.object_type === 'comment' + ? `/question/${item.question_id}?commentId=${item.comment_id}` + : ''; return ( { className="small mb-1 d-block" /> - - {item.url_title} - + {item.url_title && ( + + {item.url_title} + + )} ); }) diff --git a/ui/src/utils/localize.ts b/ui/src/utils/localize.ts index b64473a90..eea132a84 100644 --- a/ui/src/utils/localize.ts +++ b/ui/src/utils/localize.ts @@ -133,9 +133,7 @@ const localeDayjs = (langName) => { export const setupAppLanguage = async () => { const lang = getCurrentLang(); - if (!i18next.getDataByLanguage(lang)?.translation) { - await addI18nResource(lang); - } + await addI18nResource(lang); localeDayjs(lang); i18next.changeLanguage(lang); }; From ec31d18c2621a6a06604a14c5ea4bcb2e445d6c7 Mon Sep 17 00:00:00 2001 From: robin Date: Tue, 13 Aug 2024 17:42:12 +0800 Subject: [PATCH 27/64] feat(admin): Improve the badge list --- .../Admin/Badges/components/Action/index.tsx | 15 ++- ui/src/pages/Admin/Badges/index.tsx | 96 +++++++++++++------ ui/src/services/admin/badges.ts | 42 ++++++++ ui/src/services/admin/index.ts | 1 + 4 files changed, 116 insertions(+), 38 deletions(-) create mode 100644 ui/src/services/admin/badges.ts diff --git a/ui/src/pages/Admin/Badges/components/Action/index.tsx b/ui/src/pages/Admin/Badges/components/Action/index.tsx index 77601cbb4..9ec183793 100644 --- a/ui/src/pages/Admin/Badges/components/Action/index.tsx +++ b/ui/src/pages/Admin/Badges/components/Action/index.tsx @@ -23,23 +23,20 @@ import { useTranslation } from 'react-i18next'; import { Icon } from '@/components'; interface Props { - badgeData; + onSelect: (eventKey: string | null) => void; } - -const UserOperation = ({ badgeData }: Props) => { +const BadgeOperation = ({ onSelect }: Props) => { const { t } = useTranslation('translation', { keyPrefix: 'admin.badges' }); - console.log(badgeData); - return ( - + - {t('active')} - {t('deactivate')} + {t('active')} + {t('deactivate')} {t('show_logs')} @@ -48,4 +45,4 @@ const UserOperation = ({ badgeData }: Props) => { ); }; -export default UserOperation; +export default BadgeOperation; diff --git a/ui/src/pages/Admin/Badges/index.tsx b/ui/src/pages/Admin/Badges/index.tsx index 288540123..6c74f3ce4 100644 --- a/ui/src/pages/Admin/Badges/index.tsx +++ b/ui/src/pages/Admin/Badges/index.tsx @@ -22,33 +22,50 @@ import { Form, Table, Stack } from 'react-bootstrap'; import { useSearchParams } from 'react-router-dom'; import { useTranslation } from 'react-i18next'; -import { QueryGroup } from '@/components'; +import classNames from 'classnames'; + +import { Empty, Icon, Pagination, QueryGroup } from '@/components'; import * as Type from '@/common/interface'; +import { useQueryBadges, updateBadgeStatus } from '@/services/admin/badges'; import Action from './components/Action'; const BadgeFilterKeys: Type.BadgeFilterBy[] = ['all', 'active', 'inactive']; -// const bgMap = { -// normal: 'text-bg-success', -// suspended: 'text-bg-danger', -// deleted: 'text-bg-danger', -// inactive: 'text-bg-secondary', -// }; +const bgMap = { + active: 'text-bg-success', + inactive: 'text-bg-secondary', +}; + +const PAGE_SIZE = 10; const Users: FC = () => { const { t } = useTranslation('translation', { keyPrefix: 'admin.badges' }); const [urlSearchParams, setUrlSearchParams] = useSearchParams(); + const curPage = Number(urlSearchParams.get('page') || '1'); const curFilter = urlSearchParams.get('filter') || BadgeFilterKeys[0]; const curQuery = urlSearchParams.get('query') || ''; + const { data, isLoading, mutate } = useQueryBadges({ + page: curPage, + pageSize: PAGE_SIZE, + query: curQuery, + ...(curFilter === 'all' ? {} : { status: curFilter }), + }); + const handleFilter = (e) => { urlSearchParams.set('query', e.target.value); urlSearchParams.delete('page'); setUrlSearchParams(urlSearchParams); }; + const handleBadgeStatus = (badgeId, status) => { + updateBadgeStatus({ id: badgeId, status }).then(() => { + mutate(); + }); + }; + return ( <>

{t('title')}

@@ -85,36 +102,57 @@ const Users: FC = () => { - - - badge ( + + + {badge.icon?.startsWith('http') ? ( + {badge.name} + ) : ( + + )} +
+
{badge.name}
+
{badge.description}
+
+ + + {badge.group_name} + {badge.award_count} + + + {t(badge.status)} + + + handleBadgeStatus(badge.id, status)} /> -
-
Nice Question
-
Question score of 10 or more.
-
- - - Community Badges - 200 - Active - - + + ))} - {/* {Number(data?.count) <= 0 && !isLoading && } */} - {/*
+ {Number(data?.count) <= 0 && !isLoading && } +
-
*/} +
); }; diff --git a/ui/src/services/admin/badges.ts b/ui/src/services/admin/badges.ts new file mode 100644 index 000000000..b984164e2 --- /dev/null +++ b/ui/src/services/admin/badges.ts @@ -0,0 +1,42 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import qs from 'qs'; +import useSWR from 'swr'; + +import request from '@/utils/request'; +import type * as Type from '@/common/interface'; + +export const useQueryBadges = (params) => { + const apiUrl = `/answer/admin/api/badges?${qs.stringify(params)}`; + const { data, error, mutate } = useSWR( + apiUrl, + request.instance.get, + ); + return { + data, + isLoading: !data && !error, + error, + mutate, + }; +}; + +export const updateBadgeStatus = (params) => { + return request.put('/answer/admin/api/badge/status', params); +}; diff --git a/ui/src/services/admin/index.ts b/ui/src/services/admin/index.ts index ce64c5116..af83d365b 100644 --- a/ui/src/services/admin/index.ts +++ b/ui/src/services/admin/index.ts @@ -24,3 +24,4 @@ export * from './settings'; export * from './users'; export * from './dashboard'; export * from './plugins'; +export * from './badges'; From 04065a8dcb56b338c10feb9f5265ee470c92eee0 Mon Sep 17 00:00:00 2001 From: robin Date: Tue, 13 Aug 2024 18:25:52 +0800 Subject: [PATCH 28/64] refactor: Update query parameter name in badge list API call --- ui/src/pages/Admin/Badges/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/src/pages/Admin/Badges/index.tsx b/ui/src/pages/Admin/Badges/index.tsx index 6c74f3ce4..571f9b836 100644 --- a/ui/src/pages/Admin/Badges/index.tsx +++ b/ui/src/pages/Admin/Badges/index.tsx @@ -50,7 +50,7 @@ const Users: FC = () => { const { data, isLoading, mutate } = useQueryBadges({ page: curPage, pageSize: PAGE_SIZE, - query: curQuery, + q: curQuery, ...(curFilter === 'all' ? {} : { status: curFilter }), }); From 4afa76ba2a8233a949ef05f605416f5983f556c7 Mon Sep 17 00:00:00 2001 From: robin Date: Tue, 13 Aug 2024 18:31:47 +0800 Subject: [PATCH 29/64] refactor: update zh_CN.yaml --- i18n/zh_CN.yaml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/i18n/zh_CN.yaml b/i18n/zh_CN.yaml index 94e6c97f3..5612e1abd 100644 --- a/i18n/zh_CN.yaml +++ b/i18n/zh_CN.yaml @@ -1407,6 +1407,7 @@ ui: questions: 问题 answers: 回答 users: 用户管理 + badges: 徽章管理 flags: 举报管理 settings: 站点设置 general: 一般 @@ -1834,6 +1835,20 @@ ui: msg: should_be_number: 输入必须是数字 number_larger_1: 数字应该大于等于 1 + badges: + action: 操作 + active: 激活 + all: 全部 + awards: 奖励 + deactivate: 取消激活 + filter: + placeholder: "按名称筛选,徽章:id" + group: 组 + inactive: 未激活 + name: 名称 + show_logs: 显示日志 + status: 状态 + title: 徽章 form: optional: (选填) empty: 不能为空 From 342c31f38cda7c671c4939c547e424284bbda4aa Mon Sep 17 00:00:00 2001 From: shuai Date: Wed, 14 Aug 2024 16:41:14 +0800 Subject: [PATCH 30/64] fix: add link url path --- ui/src/pages/Badges/Detail/index.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/ui/src/pages/Badges/Detail/index.tsx b/ui/src/pages/Badges/Detail/index.tsx index a388d4963..46f49ce20 100644 --- a/ui/src/pages/Badges/Detail/index.tsx +++ b/ui/src/pages/Badges/Detail/index.tsx @@ -60,8 +60,6 @@ const Index = () => { return null; } - console.log(badges); - return (

{t('title')}

From 3cb1be88f6fa6c5b4bb7e476e1dada0c8e0e429b Mon Sep 17 00:00:00 2001 From: robin Date: Wed, 14 Aug 2024 14:13:28 +0800 Subject: [PATCH 31/64] refactor: Update badge description styling in Admin Badges page --- ui/src/pages/Admin/Badges/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/src/pages/Admin/Badges/index.tsx b/ui/src/pages/Admin/Badges/index.tsx index 571f9b836..f6b8cb0d0 100644 --- a/ui/src/pages/Admin/Badges/index.tsx +++ b/ui/src/pages/Admin/Badges/index.tsx @@ -127,7 +127,7 @@ const Users: FC = () => { )}
{badge.name}
-
{badge.description}
+
{badge.description}
From db1a527f955343c936b261a2832e7b071c8316d9 Mon Sep 17 00:00:00 2001 From: robin Date: Wed, 14 Aug 2024 14:19:18 +0800 Subject: [PATCH 32/64] refactor: Update page_size parameter in badge list API call --- ui/src/pages/Admin/Badges/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/src/pages/Admin/Badges/index.tsx b/ui/src/pages/Admin/Badges/index.tsx index f6b8cb0d0..448040a4a 100644 --- a/ui/src/pages/Admin/Badges/index.tsx +++ b/ui/src/pages/Admin/Badges/index.tsx @@ -49,7 +49,7 @@ const Users: FC = () => { const { data, isLoading, mutate } = useQueryBadges({ page: curPage, - pageSize: PAGE_SIZE, + page_size: PAGE_SIZE, q: curQuery, ...(curFilter === 'all' ? {} : { status: curFilter }), }); From 45f410ca4c9b06e8f8fd3041cfce46af67698ba7 Mon Sep 17 00:00:00 2001 From: robin Date: Fri, 16 Aug 2024 11:39:17 +0800 Subject: [PATCH 33/64] chore: Update tsconfig.json to exclude src/plugins directory --- ui/tsconfig.json | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/ui/tsconfig.json b/ui/tsconfig.json index b16f340ca..ac6a549eb 100644 --- a/ui/tsconfig.json +++ b/ui/tsconfig.json @@ -1,7 +1,11 @@ { "compilerOptions": { "target": "es5", - "lib": ["dom", "dom.iterable", "esnext"], + "lib": [ + "dom", + "dom.iterable", + "esnext" + ], "allowJs": true, "skipLibCheck": true, "esModuleInterop": true, @@ -20,9 +24,20 @@ "jsx": "react-jsx", "baseUrl": "./", "paths": { - "@/*": ["src/*"], - "@i18n/*": ["../i18n/*"] + "@/*": [ + "src/*" + ], + "@i18n/*": [ + "../i18n/*" + ] } }, - "include": ["src", "node_modules/@testing-library/jest-dom", "scripts" ] -} + "include": [ + "src", + "node_modules/@testing-library/jest-dom", + "scripts" + ], + "exclude": [ + "src/plugins" + ] +} \ No newline at end of file From ac28c8871251c28fcdf9711922bc549c93ee7f10 Mon Sep 17 00:00:00 2001 From: robin Date: Fri, 16 Aug 2024 11:41:11 +0800 Subject: [PATCH 34/64] refactor: Update badge description styling in Admin Badges page --- i18n/en_US.yaml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/i18n/en_US.yaml b/i18n/en_US.yaml index 420d25bac..8b137d373 100644 --- a/i18n/en_US.yaml +++ b/i18n/en_US.yaml @@ -1580,7 +1580,7 @@ ui: visited_x_days: "Visited {{ count }} days" viewed: Viewed joined: Joined - comma: ',' + comma: "," last_login: Seen about_me: About Me about_me_empty: "// Hello, World !" @@ -1738,6 +1738,11 @@ ui: qrcode_login_tip: Please use {{ agentName }} to scan the QR code and log in. login_failed_email_tip: Login failed, please allow this app to access your email information before try again. badges: + modal: + title: Congratulations + content: You've earned a new badge. + close: Close + confirm: View badges title: Badges awarded: Awarded earned_x: Earned x{{ number }} From 90cf954d8fc28103f15079c102ae4ac0a6b9cab7 Mon Sep 17 00:00:00 2001 From: robin Date: Fri, 16 Aug 2024 11:41:57 +0800 Subject: [PATCH 35/64] refactor: Update badge description styling in Admin Badges page --- ui/src/common/interface.ts | 15 +++- ui/src/components/Modal/BadgeModal.tsx | 70 +++++++++++++++++++ ui/src/components/Modal/index.tsx | 3 +- ui/src/pages/Admin/Badges/index.tsx | 6 +- ui/src/pages/Layout/index.tsx | 8 ++- .../components/Achievements/index.scss | 5 +- .../components/Achievements/index.tsx | 27 +++++-- ui/src/services/admin/badges.ts | 8 +-- 8 files changed, 124 insertions(+), 18 deletions(-) create mode 100644 ui/src/components/Modal/BadgeModal.tsx diff --git a/ui/src/common/interface.ts b/ui/src/common/interface.ts index 608a2465e..a06fec280 100644 --- a/ui/src/common/interface.ts +++ b/ui/src/common/interface.ts @@ -220,11 +220,19 @@ export interface SetNoticeReq { notice_switch: boolean; } +export interface NotificationBadgeAward { + notification_id: string; + badge_id: string; + name: string; + icon: string; + level: number; +} export interface NotificationStatus { inbox: number; achievement: number; revision: number; can_revision: boolean; + badge_award: NotificationBadgeAward | null; } export interface QuestionDetailRes { @@ -758,13 +766,18 @@ export interface BadgeInfo extends BadgeListItem { is_single: boolean; } +export interface AdminBadgeListItem extends BadgeListItem { + group_name: string; + status: string; + description: string; +} + export interface BadgeDetailListReq { page: number; page_size: number; badge_id: string; username?: string | null; } - export interface BadgeDetailListItem { created_at: number; author_user_info: UserInfoBase; diff --git a/ui/src/components/Modal/BadgeModal.tsx b/ui/src/components/Modal/BadgeModal.tsx new file mode 100644 index 000000000..214c1d67f --- /dev/null +++ b/ui/src/components/Modal/BadgeModal.tsx @@ -0,0 +1,70 @@ +import { FC } from 'react'; +import { useTranslation } from 'react-i18next'; +import { useNavigate } from 'react-router-dom'; + +import classNames from 'classnames'; + +import type * as Type from '@/common/interface'; +import { loggedUserInfoStore } from '@/stores'; +import { readNotification, useQueryNotificationStatus } from '@/services'; +import Icon from '../Icon'; + +import Modal from './Modal'; + +interface BadgeModalProps { + badge?: Type.NotificationBadgeAward | null; + visible: boolean; +} +const BadgeModal: FC = ({ badge, visible }) => { + const { t } = useTranslation('translation', { keyPrefix: 'badges.modal' }); + const { user } = loggedUserInfoStore(); + const navigate = useNavigate(); + const { data } = useQueryNotificationStatus(); + + const handleCancel = async () => { + if (!data) return; + await readNotification(badge?.notification_id); + }; + const handleConfirm = async () => { + await readNotification(badge?.notification_id); + + const url = `/badges/${badge?.badge_id}?username=${user.username}`; + navigate(url); + }; + + return ( + + {badge && ( +
+ {badge.icon?.startsWith('http') ? ( + {badge.name} + ) : ( + + )} +
{badge?.name}
+

{t('content')}

+
+ )} +
+ ); +}; + +export default BadgeModal; diff --git a/ui/src/components/Modal/index.tsx b/ui/src/components/Modal/index.tsx index 404591966..75d968421 100644 --- a/ui/src/components/Modal/index.tsx +++ b/ui/src/components/Modal/index.tsx @@ -20,6 +20,7 @@ import DefaultModal from './Modal'; import confirm, { Config } from './Confirm'; import LoginToContinueModal from './LoginToContinueModal'; +import BadgeModal from './BadgeModal'; type ModalType = typeof DefaultModal & { confirm: (config: Config) => void; @@ -32,4 +33,4 @@ Modal.confirm = function (props: Config) { export default Modal; -export { LoginToContinueModal }; +export { LoginToContinueModal, BadgeModal }; diff --git a/ui/src/pages/Admin/Badges/index.tsx b/ui/src/pages/Admin/Badges/index.tsx index 448040a4a..e76f90eba 100644 --- a/ui/src/pages/Admin/Badges/index.tsx +++ b/ui/src/pages/Admin/Badges/index.tsx @@ -39,7 +39,7 @@ const bgMap = { const PAGE_SIZE = 10; -const Users: FC = () => { +const Badges: FC = () => { const { t } = useTranslation('translation', { keyPrefix: 'admin.badges' }); const [urlSearchParams, setUrlSearchParams] = useSearchParams(); @@ -103,7 +103,7 @@ const Users: FC = () => { {data?.list.map((badge) => ( - + {badge.icon?.startsWith('http') ? ( { ); }; -export default Users; +export default Badges; diff --git a/ui/src/pages/Layout/index.tsx b/ui/src/pages/Layout/index.tsx index 2029b0fa4..e8bab2799 100644 --- a/ui/src/pages/Layout/index.tsx +++ b/ui/src/pages/Layout/index.tsx @@ -33,8 +33,9 @@ import { PageTags, HttpErrorContent, } from '@/components'; -import { LoginToContinueModal } from '@/components/Modal'; +import { LoginToContinueModal, BadgeModal } from '@/components/Modal'; import { changeTheme } from '@/utils'; +import { useQueryNotificationStatus } from '@/services'; const Layout: FC = () => { const location = useLocation(); @@ -44,6 +45,7 @@ const Layout: FC = () => { }; const { code: httpStatusCode, reset: httpStatusReset } = errorCodeStore(); const { show: showLoginToContinueModal } = loginToContinueStore(); + const { data: notificationData } = useQueryNotificationStatus(); useEffect(() => { httpStatusReset(); @@ -86,6 +88,10 @@ const Layout: FC = () => {
+ diff --git a/ui/src/pages/Users/Notifications/components/Achievements/index.scss b/ui/src/pages/Users/Notifications/components/Achievements/index.scss index fb91193fb..9a2249215 100644 --- a/ui/src/pages/Users/Notifications/components/Achievements/index.scss +++ b/ui/src/pages/Users/Notifications/components/Achievements/index.scss @@ -18,8 +18,9 @@ */ .achievement-wrap { - .num { + .num, + .icon { width: 60px; flex: none; } -} +} \ No newline at end of file diff --git a/ui/src/pages/Users/Notifications/components/Achievements/index.tsx b/ui/src/pages/Users/Notifications/components/Achievements/index.tsx index 5f2e8f58c..c171a4273 100644 --- a/ui/src/pages/Users/Notifications/components/Achievements/index.tsx +++ b/ui/src/pages/Users/Notifications/components/Achievements/index.tsx @@ -24,10 +24,13 @@ import classNames from 'classnames'; import isEmpty from 'lodash/isEmpty'; import { Empty } from '@/components'; +import { loggedUserInfoStore } from '@/stores'; import './index.scss'; const Achievements = ({ data, handleReadNotification }) => { + const { user } = loggedUserInfoStore(); + if (!data) { return null; } @@ -50,6 +53,9 @@ const Achievements = ({ data, handleReadNotification }) => { case 'comment': url = `/questions/${question}/${answer}?commentId=${comment}`; break; + case 'badge_award': + url = `/badges/${item.object_info.object_map.badge_id}?username=${user.username}`; + break; default: url = ''; } @@ -60,13 +66,22 @@ const Achievements = ({ data, handleReadNotification }) => { 'd-flex border-start-0 border-end-0 py-3', !item.is_read && 'warning', )}> - {item.rank > 0 && ( -
{`+${item.rank}`}
- )} - {item.rank === 0 &&
{item.rank}
} - {item.rank < 0 && ( -
{`${item.rank}`}
+ {item.object_info.object_type === 'badge_award' ? ( +
👏
+ ) : ( + <> + {item.rank > 0 && ( +
{`+${item.rank}`}
+ )} + {item.rank === 0 && ( +
{item.rank}
+ )} + {item.rank < 0 && ( +
{`${item.rank}`}
+ )} + )} +
handleReadNotification(item.id)}> {item.object_info.title} diff --git a/ui/src/services/admin/badges.ts b/ui/src/services/admin/badges.ts index b984164e2..64685bc5e 100644 --- a/ui/src/services/admin/badges.ts +++ b/ui/src/services/admin/badges.ts @@ -25,10 +25,10 @@ import type * as Type from '@/common/interface'; export const useQueryBadges = (params) => { const apiUrl = `/answer/admin/api/badges?${qs.stringify(params)}`; - const { data, error, mutate } = useSWR( - apiUrl, - request.instance.get, - ); + const { data, error, mutate } = useSWR< + Type.ListResult, + Error + >(apiUrl, request.instance.get); return { data, isLoading: !data && !error, From 85516530ca1d98710f05194da0305a227318244e Mon Sep 17 00:00:00 2001 From: robin Date: Tue, 13 Aug 2024 15:02:29 +0800 Subject: [PATCH 36/64] =?UTF-8?q?feat=EF=BC=9ASupport=20Render=20type=20pl?= =?UTF-8?q?ugins?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ui/src/utils/pluginKit/index.ts | 3 ++- ui/src/utils/pluginKit/interface.ts | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/ui/src/utils/pluginKit/index.ts b/ui/src/utils/pluginKit/index.ts index 1f7efacab..26e3a2883 100644 --- a/ui/src/utils/pluginKit/index.ts +++ b/ui/src/utils/pluginKit/index.ts @@ -221,7 +221,8 @@ const useRenderHtmlPlugin = ( return ( plugin.activated && plugin.hooks?.useRender && - plugin.info.type === PluginType.Editor + (plugin.info.type === PluginType.Editor || + plugin.info.type === PluginType.Render) ); }) .forEach((plugin) => { diff --git a/ui/src/utils/pluginKit/interface.ts b/ui/src/utils/pluginKit/interface.ts index e1b8ab0c9..462ae47b2 100644 --- a/ui/src/utils/pluginKit/interface.ts +++ b/ui/src/utils/pluginKit/interface.ts @@ -27,6 +27,7 @@ export enum PluginType { Editor = 'editor', Route = 'route', Captcha = 'captcha', + Render = 'render', } export interface PluginInfo { @@ -45,7 +46,7 @@ export interface Plugin { useRender?: Array< (element: HTMLElement | RefObject | null) => void >; - useCaptcha?: (props: { captchaKey: Type.CaptchaKey; commonProps: any }) => { + useCaptcha?: (props: { captchaKey: Type.CaptchaKey; commonProps: any; }) => { getCaptcha: () => Record; check: (t: () => void) => void; handleCaptchaError: (error) => any; From 7c59d3f3cb167abdaadf0a570dec24c11efc9cc3 Mon Sep 17 00:00:00 2001 From: robin Date: Thu, 15 Aug 2024 10:57:33 +0800 Subject: [PATCH 37/64] feat: Add useRenderPlugin to Editor component --- ui/src/components/Editor/index.tsx | 4 +++- ui/src/utils/pluginKit/index.ts | 28 +++++++++++++++++++++++++++- ui/src/utils/pluginKit/interface.ts | 2 +- 3 files changed, 31 insertions(+), 3 deletions(-) diff --git a/ui/src/components/Editor/index.tsx b/ui/src/components/Editor/index.tsx index dd0040219..ead37653d 100644 --- a/ui/src/components/Editor/index.tsx +++ b/ui/src/components/Editor/index.tsx @@ -27,7 +27,7 @@ import { import classNames from 'classnames'; -import { PluginType } from '@/utils/pluginKit'; +import { PluginType, useRenderPlugin } from '@/utils/pluginKit'; import PluginRender from '../PluginRender'; import { @@ -84,6 +84,8 @@ const MDEditor: ForwardRefRenderFunction = ( const editorRef = useRef(null); const previewRef = useRef<{ getHtml; element } | null>(null); + useRenderPlugin(previewRef.current?.element); + const editor = useEditor({ editorRef, onChange, diff --git a/ui/src/utils/pluginKit/index.ts b/ui/src/utils/pluginKit/index.ts index 26e3a2883..6d0e9878a 100644 --- a/ui/src/utils/pluginKit/index.ts +++ b/ui/src/utils/pluginKit/index.ts @@ -232,6 +232,26 @@ const useRenderHtmlPlugin = ( }); }; +// Only for render type plugins +const useRenderPlugin = ( + element: HTMLElement | RefObject | null, +) => { + return plugins + .getPlugins() + .filter((plugin) => { + return ( + plugin.activated && + plugin.hooks?.useRender && + plugin.info.type === PluginType.Render + ); + }) + .forEach((plugin) => { + plugin.hooks?.useRender?.forEach((hook) => { + hook(element); + }); + }); +}; + // Only one captcha type plug-in can be enabled at the same time const useCaptchaPlugin = (key: Type.CaptchaKey) => { const captcha = plugins @@ -248,5 +268,11 @@ const useCaptchaPlugin = (key: Type.CaptchaKey) => { export type { Plugin, PluginInfo }; -export { useRenderHtmlPlugin, mergeRoutePlugins, useCaptchaPlugin, PluginType }; +export { + useRenderHtmlPlugin, + mergeRoutePlugins, + useCaptchaPlugin, + useRenderPlugin, + PluginType, +}; export default plugins; diff --git a/ui/src/utils/pluginKit/interface.ts b/ui/src/utils/pluginKit/interface.ts index 462ae47b2..880a524a5 100644 --- a/ui/src/utils/pluginKit/interface.ts +++ b/ui/src/utils/pluginKit/interface.ts @@ -46,7 +46,7 @@ export interface Plugin { useRender?: Array< (element: HTMLElement | RefObject | null) => void >; - useCaptcha?: (props: { captchaKey: Type.CaptchaKey; commonProps: any; }) => { + useCaptcha?: (props: { captchaKey: Type.CaptchaKey; commonProps: any }) => { getCaptcha: () => Record; check: (t: () => void) => void; handleCaptchaError: (error) => any; From e48a13fed51f9f14a28d914ea3ca75920dc49333 Mon Sep 17 00:00:00 2001 From: robin Date: Thu, 22 Aug 2024 10:04:47 +0800 Subject: [PATCH 38/64] refactor: Update Chinese translation for badge activation --- i18n/zh_CN.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/i18n/zh_CN.yaml b/i18n/zh_CN.yaml index 5612e1abd..207825167 100644 --- a/i18n/zh_CN.yaml +++ b/i18n/zh_CN.yaml @@ -1840,7 +1840,7 @@ ui: active: 激活 all: 全部 awards: 奖励 - deactivate: 取消激活 + deactivate: 停用 filter: placeholder: "按名称筛选,徽章:id" group: 组 From e4d0322a1694b026a205f0ddc3221e249fe6280a Mon Sep 17 00:00:00 2001 From: robin Date: Thu, 22 Aug 2024 10:04:52 +0800 Subject: [PATCH 39/64] refactor: Remove unused dropdown item in BadgeOperation component --- ui/src/pages/Admin/Badges/components/Action/index.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/ui/src/pages/Admin/Badges/components/Action/index.tsx b/ui/src/pages/Admin/Badges/components/Action/index.tsx index 9ec183793..b5bf8ddd7 100644 --- a/ui/src/pages/Admin/Badges/components/Action/index.tsx +++ b/ui/src/pages/Admin/Badges/components/Action/index.tsx @@ -37,8 +37,6 @@ const BadgeOperation = ({ onSelect }: Props) => { {t('active')} {t('deactivate')} - - {t('show_logs')} From e6aa178062c65cd0f79671efd6ebda90219f3843 Mon Sep 17 00:00:00 2001 From: robin Date: Thu, 22 Aug 2024 10:45:24 +0800 Subject: [PATCH 40/64] refactor: Update key prop in BadgeDetail component --- ui/src/pages/Badges/Detail/index.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/ui/src/pages/Badges/Detail/index.tsx b/ui/src/pages/Badges/Detail/index.tsx index 46f49ce20..f8a9327af 100644 --- a/ui/src/pages/Badges/Detail/index.tsx +++ b/ui/src/pages/Badges/Detail/index.tsx @@ -80,7 +80,12 @@ const Index = () => { ? `/question/${item.question_id}?commentId=${item.comment_id}` : ''; return ( - + Date: Thu, 22 Aug 2024 10:47:44 +0800 Subject: [PATCH 41/64] refactor: Update BadgeModal component to handle badge cancellation and confirmation --- ui/src/components/Modal/BadgeModal.tsx | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/ui/src/components/Modal/BadgeModal.tsx b/ui/src/components/Modal/BadgeModal.tsx index 214c1d67f..32049de6d 100644 --- a/ui/src/components/Modal/BadgeModal.tsx +++ b/ui/src/components/Modal/BadgeModal.tsx @@ -19,14 +19,21 @@ const BadgeModal: FC = ({ badge, visible }) => { const { t } = useTranslation('translation', { keyPrefix: 'badges.modal' }); const { user } = loggedUserInfoStore(); const navigate = useNavigate(); - const { data } = useQueryNotificationStatus(); + const { data, mutate } = useQueryNotificationStatus(); - const handleCancel = async () => { + const handle = async () => { if (!data) return; await readNotification(badge?.notification_id); + await mutate({ + ...data, + badge_award: null, + }); + }; + const handleCancel = async () => { + await handle(); }; const handleConfirm = async () => { - await readNotification(badge?.notification_id); + await handle(); const url = `/badges/${badge?.badge_id}?username=${user.username}`; navigate(url); From b20e656c30b3a3dfead266c36397da78bdff4c69 Mon Sep 17 00:00:00 2001 From: LinkinStars Date: Thu, 22 Aug 2024 14:21:18 +0800 Subject: [PATCH 42/64] feat(config): add deactivate plugin config cli --- cmd/command.go | 11 ++++++++--- internal/cli/config.go | 39 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 3 deletions(-) diff --git a/cmd/command.go b/cmd/command.go index b8d9c5611..65710e6b4 100644 --- a/cmd/command.go +++ b/cmd/command.go @@ -263,12 +263,17 @@ To run answer, use: } field := &cli.ConfigField{} - for _, f := range configFields { - switch f { + fmt.Println(configFields) + if len(configFields) > 0 { + switch configFields[0] { case "allow_password_login": field.AllowPasswordLogin = true + case "deactivate_plugin": + if len(configFields) > 1 { + field.DeactivatePluginSlugName = configFields[1] + } default: - fmt.Printf("field %s not support\n", f) + fmt.Printf("field %s not support\n", configFields[0]) } } err = cli.SetDefaultConfig(c.Data.Database, c.Data.Cache, field) diff --git a/internal/cli/config.go b/internal/cli/config.go index 06764e41a..9ebefa1da 100644 --- a/internal/cli/config.go +++ b/internal/cli/config.go @@ -31,6 +31,8 @@ import ( type ConfigField struct { AllowPasswordLogin bool `json:"allow_password_login"` + // The slug name of plugin that you want to deactivate + DeactivatePluginSlugName string `json:"deactivate_plugin_slug_name"` } // SetDefaultConfig set default config @@ -55,6 +57,9 @@ func SetDefaultConfig(dbConf *data.Database, cacheConf *data.CacheConf, field *C if field.AllowPasswordLogin { return defaultLoginConfig(db) } + if len(field.DeactivatePluginSlugName) > 0 { + return deactivatePlugin(db, field.DeactivatePluginSlugName) + } return nil } @@ -82,3 +87,37 @@ func defaultLoginConfig(x *xorm.Engine) (err error) { } return nil } + +func deactivatePlugin(x *xorm.Engine, pluginSlugName string) (err error) { + fmt.Printf("try to deactivate plugin: %s\n", pluginSlugName) + + item := &entity.Config{Key: constant.PluginStatus} + exist, err := x.Get(item) + if err != nil { + return fmt.Errorf("get config failed: %w", err) + } + if !exist { + return nil + } + + pluginStatusMapping := make(map[string]bool) + _ = json.Unmarshal([]byte(item.Value), &pluginStatusMapping) + status, ok := pluginStatusMapping[pluginSlugName] + if !ok { + fmt.Printf("plugin %s not exist\n", pluginSlugName) + return nil + } + if !status { + fmt.Printf("plugin %s already deactivated\n", pluginSlugName) + return nil + } + + pluginStatusMapping[pluginSlugName] = false + dataByte, _ := json.Marshal(pluginStatusMapping) + item.Value = string(dataByte) + _, err = x.ID(item.ID).Cols("value").Update(item) + if err != nil { + return fmt.Errorf("update plugin status failed: %w", err) + } + return nil +} From 76d455e6de6907ff49fea25071113b4cf4782c53 Mon Sep 17 00:00:00 2001 From: robin Date: Thu, 22 Aug 2024 14:21:47 +0800 Subject: [PATCH 43/64] refactor: Restore zh_CN.yaml --- i18n/zh_CN.yaml | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/i18n/zh_CN.yaml b/i18n/zh_CN.yaml index 207825167..396c2f9b3 100644 --- a/i18n/zh_CN.yaml +++ b/i18n/zh_CN.yaml @@ -1407,7 +1407,6 @@ ui: questions: 问题 answers: 回答 users: 用户管理 - badges: 徽章管理 flags: 举报管理 settings: 站点设置 general: 一般 @@ -1835,20 +1834,6 @@ ui: msg: should_be_number: 输入必须是数字 number_larger_1: 数字应该大于等于 1 - badges: - action: 操作 - active: 激活 - all: 全部 - awards: 奖励 - deactivate: 停用 - filter: - placeholder: "按名称筛选,徽章:id" - group: 组 - inactive: 未激活 - name: 名称 - show_logs: 显示日志 - status: 状态 - title: 徽章 form: optional: (选填) empty: 不能为空 @@ -1935,4 +1920,4 @@ ui: post_reopen: 这个帖子已被重新打开. post_list: 这个帖子已经被显示 post_unlist: 这个帖子已经被隐藏 - post_pending: 您的帖子正在等待审核。它将在它获得批准后可见。 + post_pending: 您的帖子正在等待审核。它将在它获得批准后可见。 \ No newline at end of file From 1e8072ba21951890a24c2c448638beadedf2112c Mon Sep 17 00:00:00 2001 From: robin Date: Thu, 22 Aug 2024 14:37:49 +0800 Subject: [PATCH 44/64] refactor: Update translation for achievements --- i18n/en_US.yaml | 3 +++ .../Users/Notifications/components/Achievements/index.tsx | 4 +++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/i18n/en_US.yaml b/i18n/en_US.yaml index 8b137d373..032ad1417 100644 --- a/i18n/en_US.yaml +++ b/i18n/en_US.yaml @@ -853,6 +853,9 @@ ui: posts: Posts invites: Invites votes: Votes + answer: Answer + question: Question + badge_award: Badge suspended: title: Your Account has been Suspended until_time: "Your account was suspended until {{ time }}." diff --git a/ui/src/pages/Users/Notifications/components/Achievements/index.tsx b/ui/src/pages/Users/Notifications/components/Achievements/index.tsx index c171a4273..824bb1788 100644 --- a/ui/src/pages/Users/Notifications/components/Achievements/index.tsx +++ b/ui/src/pages/Users/Notifications/components/Achievements/index.tsx @@ -19,6 +19,7 @@ import { ListGroup } from 'react-bootstrap'; import { Link } from 'react-router-dom'; +import { useTranslation } from 'react-i18next'; import classNames from 'classnames'; import isEmpty from 'lodash/isEmpty'; @@ -30,6 +31,7 @@ import './index.scss'; const Achievements = ({ data, handleReadNotification }) => { const { user } = loggedUserInfoStore(); + const { t } = useTranslation('translation', { keyPrefix: 'notifications' }); if (!data) { return null; @@ -87,7 +89,7 @@ const Achievements = ({ data, handleReadNotification }) => { {item.object_info.title} - {item.object_info.object_type} + {t(item.object_info.object_type)}
From 065bf980d34ea50574b713aff4e4d2f1b4d136c4 Mon Sep 17 00:00:00 2001 From: robin Date: Thu, 22 Aug 2024 15:50:40 +0800 Subject: [PATCH 45/64] chore: Add License --- ui/src/components/CardBadge/index.tsx | 1 + ui/src/components/Modal/BadgeModal.tsx | 19 +++++++++++++++++++ ui/src/pages/Badges/index.tsx | 1 + 3 files changed, 21 insertions(+) diff --git a/ui/src/components/CardBadge/index.tsx b/ui/src/components/CardBadge/index.tsx index 0983f108f..dd8c18e61 100644 --- a/ui/src/components/CardBadge/index.tsx +++ b/ui/src/components/CardBadge/index.tsx @@ -16,6 +16,7 @@ * specific language governing permissions and limitations * under the License. */ + import { useTranslation } from 'react-i18next'; import { FC } from 'react'; import { Card, Badge } from 'react-bootstrap'; diff --git a/ui/src/components/Modal/BadgeModal.tsx b/ui/src/components/Modal/BadgeModal.tsx index 32049de6d..468c7f892 100644 --- a/ui/src/components/Modal/BadgeModal.tsx +++ b/ui/src/components/Modal/BadgeModal.tsx @@ -1,3 +1,22 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + import { FC } from 'react'; import { useTranslation } from 'react-i18next'; import { useNavigate } from 'react-router-dom'; diff --git a/ui/src/pages/Badges/index.tsx b/ui/src/pages/Badges/index.tsx index d37f5d9a8..396280620 100644 --- a/ui/src/pages/Badges/index.tsx +++ b/ui/src/pages/Badges/index.tsx @@ -16,6 +16,7 @@ * specific language governing permissions and limitations * under the License. */ + import { useTranslation } from 'react-i18next'; import { CardBadge } from '@/components'; From 82d7445e62321a6cc859df15007a486b5a70b5ad Mon Sep 17 00:00:00 2001 From: kumfo Date: Thu, 22 Aug 2024 16:13:44 +0800 Subject: [PATCH 46/64] fix: embed controller add error return --- internal/controller/embed_controller.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/internal/controller/embed_controller.go b/internal/controller/embed_controller.go index a61bd68cf..eb54f1a67 100644 --- a/internal/controller/embed_controller.go +++ b/internal/controller/embed_controller.go @@ -43,10 +43,10 @@ func NewEmbedController() *EmbedController { func (c *EmbedController) GetEmbedConfig(ctx *gin.Context) { resp := make([]*plugin.EmbedConfig, 0) - _ = plugin.CallEmbed(func(embed plugin.Embed) (err error) { + err := plugin.CallEmbed(func(embed plugin.Embed) (err error) { resp, err = embed.GetEmbedConfigs(ctx) - return nil + return err }) - handler.HandleResponse(ctx, nil, resp) + handler.HandleResponse(ctx, err, resp) } From 1af1f77a60afb2adec3034e06118c0d70610fffd Mon Sep 17 00:00:00 2001 From: robin Date: Fri, 23 Aug 2024 11:10:31 +0800 Subject: [PATCH 47/64] feat: Add RenderController to handle rendering configuration --- cmd/wire_gen.go | 3 +- internal/controller/controller.go | 1 + internal/controller/render_controller.go | 52 ++++++++++++++++++++++++ internal/router/plugin_api_router.go | 4 ++ plugin/plugin.go | 4 ++ plugin/render.go | 39 ++++++++++++++++++ 6 files changed, 102 insertions(+), 1 deletion(-) create mode 100644 internal/controller/render_controller.go create mode 100644 plugin/render.go diff --git a/cmd/wire_gen.go b/cmd/wire_gen.go index cc245aea5..940c0dc10 100644 --- a/cmd/wire_gen.go +++ b/cmd/wire_gen.go @@ -282,7 +282,8 @@ func initApplication(debug bool, serverConf *conf.Server, dbConf *data.Database, userCenterController := controller.NewUserCenterController(userCenterLoginService, siteInfoCommonService) captchaController := controller.NewCaptchaController() embedController := controller.NewEmbedController() - pluginAPIRouter := router.NewPluginAPIRouter(connectorController, userCenterController, captchaController, embedController) + renderController := controller.NewRenderController() + pluginAPIRouter := router.NewPluginAPIRouter(connectorController, userCenterController, captchaController, embedController, renderController) ginEngine := server.NewHTTPServer(debug, staticRouter, answerAPIRouter, swaggerRouter, uiRouter, authUserMiddleware, avatarMiddleware, shortIDMiddleware, templateRouter, pluginAPIRouter, uiConf) scheduledTaskManager := cron.NewScheduledTaskManager(siteInfoCommonService, questionService) application := newApplication(serverConf, ginEngine, scheduledTaskManager) diff --git a/internal/controller/controller.go b/internal/controller/controller.go index 8fad918a1..cbf80f7fa 100644 --- a/internal/controller/controller.go +++ b/internal/controller/controller.go @@ -52,4 +52,5 @@ var ProviderSetController = wire.NewSet( NewMetaController, NewEmbedController, NewBadgeController, + NewRenderController, ) diff --git a/internal/controller/render_controller.go b/internal/controller/render_controller.go new file mode 100644 index 000000000..05e49d0b7 --- /dev/null +++ b/internal/controller/render_controller.go @@ -0,0 +1,52 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package controller + +import ( + "github.com/apache/incubator-answer/internal/base/handler" + "github.com/apache/incubator-answer/plugin" + "github.com/gin-gonic/gin" +) + +type RenderController struct { +} + +func NewRenderController() *RenderController { + return &RenderController{} +} + +// GetRenderConfig godoc +// @Summary GetRenderConfig +// @Description GetRenderConfig +// @Tags PluginRender +// @Accept json +// @Produce json +// @Router /answer/api/v1/render/config [get] +// @Success 200 {object} handler.RespBody{data=plugin.RenderConfig} +func (c *RenderController) GetRenderConfig(ctx *gin.Context) { + var resp *plugin.RenderConfig + + _ = plugin.CallRender(func(render plugin.Render) (err error) { + resp = render.GetRenderConfig(ctx) + return nil + }) + + handler.HandleResponse(ctx, nil, resp) +} diff --git a/internal/router/plugin_api_router.go b/internal/router/plugin_api_router.go index e4a85a154..e69e0b6b0 100644 --- a/internal/router/plugin_api_router.go +++ b/internal/router/plugin_api_router.go @@ -29,6 +29,7 @@ type PluginAPIRouter struct { userCenterController *controller.UserCenterController captchaController *controller.CaptchaController embedController *controller.EmbedController + renderController *controller.RenderController } func NewPluginAPIRouter( @@ -36,12 +37,14 @@ func NewPluginAPIRouter( userCenterController *controller.UserCenterController, captchaController *controller.CaptchaController, embedController *controller.EmbedController, + renderController *controller.RenderController, ) *PluginAPIRouter { return &PluginAPIRouter{ connectorController: connectorController, userCenterController: userCenterController, captchaController: captchaController, embedController: embedController, + renderController: renderController, } } @@ -64,6 +67,7 @@ func (pr *PluginAPIRouter) RegisterUnAuthConnectorRouter(r *gin.RouterGroup) { // captcha plugin r.GET("/captcha/config", pr.captchaController.GetCaptchaConfig) r.GET("/embed/config", pr.embedController.GetEmbedConfig) + r.GET("/render/config", pr.renderController.GetRenderConfig) } func (pr *PluginAPIRouter) RegisterAuthUserConnectorRouter(r *gin.RouterGroup) { diff --git a/plugin/plugin.go b/plugin/plugin.go index 852868557..9c144346a 100644 --- a/plugin/plugin.go +++ b/plugin/plugin.go @@ -103,6 +103,10 @@ func Register(p Base) { registerEmbed(p.(Embed)) } + if _, ok := p.(Render); ok { + registerRender(p.(Render)) + } + if _, ok := p.(CDN); ok { registerCDN(p.(CDN)) } diff --git a/plugin/render.go b/plugin/render.go new file mode 100644 index 000000000..4fc2edf6f --- /dev/null +++ b/plugin/render.go @@ -0,0 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package plugin + +import "github.com/gin-gonic/gin" + +type RenderConfig struct { + SelectTheme string `json:"select_theme"` +} + +// select_theme + +type Render interface { + Base + GetRenderConfig(ctx *gin.Context) (renderConfig *RenderConfig) +} + +var ( + // CallReviewer is a function that calls all registered parsers + CallRender, + registerRender = MakePlugin[Render](false) +) From 6214fed8ae75ac01df5ec937348e295b032e29ed Mon Sep 17 00:00:00 2001 From: robin Date: Fri, 23 Aug 2024 11:24:41 +0800 Subject: [PATCH 48/64] docs: Add render api --- docs/docs.go | 53 +++++++++++++++++++++++++++++++++++++++++++++-- docs/swagger.json | 53 +++++++++++++++++++++++++++++++++++++++++++++-- docs/swagger.yaml | 31 ++++++++++++++++++++++++++- 3 files changed, 132 insertions(+), 5 deletions(-) diff --git a/docs/docs.go b/docs/docs.go index daef68513..389f3bdfa 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -4854,6 +4854,41 @@ const docTemplate = `{ } } }, + "/answer/api/v1/render/config": { + "get": { + "description": "GetRenderConfig", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "PluginRender" + ], + "summary": "GetRenderConfig", + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/handler.RespBody" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/plugin.RenderConfig" + } + } + } + ] + } + } + } + } + }, "/answer/api/v1/report": { "post": { "security": [ @@ -7420,6 +7455,14 @@ const docTemplate = `{ } } }, + "plugin.RenderConfig": { + "type": "object", + "properties": { + "select_theme": { + "type": "string" + } + } + }, "schema.AcceptAnswerReq": { "type": "object", "required": [ @@ -9257,10 +9300,16 @@ const docTemplate = `{ }, "schema.NotificationClearRequest": { "type": "object", + "required": [ + "type" + ], "properties": { "type": { - "description": "inbox achievement", - "type": "string" + "type": "string", + "enum": [ + "inbox", + "achievement" + ] } } }, diff --git a/docs/swagger.json b/docs/swagger.json index c7ad452e2..3d6f07c8d 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -4828,6 +4828,41 @@ } } }, + "/answer/api/v1/render/config": { + "get": { + "description": "GetRenderConfig", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "PluginRender" + ], + "summary": "GetRenderConfig", + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/handler.RespBody" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/plugin.RenderConfig" + } + } + } + ] + } + } + } + } + }, "/answer/api/v1/report": { "post": { "security": [ @@ -7394,6 +7429,14 @@ } } }, + "plugin.RenderConfig": { + "type": "object", + "properties": { + "select_theme": { + "type": "string" + } + } + }, "schema.AcceptAnswerReq": { "type": "object", "required": [ @@ -9231,10 +9274,16 @@ }, "schema.NotificationClearRequest": { "type": "object", + "required": [ + "type" + ], "properties": { "type": { - "description": "inbox achievement", - "type": "string" + "type": "string", + "enum": [ + "inbox", + "achievement" + ] } } }, diff --git a/docs/swagger.yaml b/docs/swagger.yaml index 1cf298627..d2d435041 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -135,6 +135,11 @@ definitions: platform: type: string type: object + plugin.RenderConfig: + properties: + select_theme: + type: string + type: object schema.AcceptAnswerReq: properties: answer_id: @@ -1414,8 +1419,12 @@ definitions: schema.NotificationClearRequest: properties: type: - description: inbox achievement + enum: + - inbox + - achievement type: string + required: + - type type: object schema.OnCompleteAction: properties: @@ -5817,6 +5826,26 @@ paths: summary: get reasons by object type and action tags: - reason + /answer/api/v1/render/config: + get: + consumes: + - application/json + description: GetRenderConfig + produces: + - application/json + responses: + "200": + description: OK + schema: + allOf: + - $ref: '#/definitions/handler.RespBody' + - properties: + data: + $ref: '#/definitions/plugin.RenderConfig' + type: object + summary: GetRenderConfig + tags: + - PluginRender /answer/api/v1/report: post: consumes: From 16605eafd0008ad42537ca4aacbf6e4a250c9c7e Mon Sep 17 00:00:00 2001 From: Jan Ernsting Date: Fri, 23 Aug 2024 09:18:56 +0200 Subject: [PATCH 49/64] Fix error on multiple make generate runs (#1087) As discussed in #1074 --------- Co-authored-by: Fen --- Makefile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Makefile b/Makefile index 9dc8c983e..bcacf3e46 100644 --- a/Makefile +++ b/Makefile @@ -23,6 +23,8 @@ universal: generate generate: @$(GO) get github.com/google/wire/cmd/wire@v0.5.0 @$(GO) get github.com/golang/mock/mockgen@v1.6.0 + @$(GO) get github.com/swaggo/swag/cmd/swag@v1.16.3 + @$(GO) install github.com/swaggo/swag/cmd/swag@v1.16.3 @$(GO) install github.com/google/wire/cmd/wire@v0.5.0 @$(GO) install github.com/golang/mock/mockgen@v1.6.0 @$(GO) generate ./... From 17c69c3cc55774f531ab4477e2c022b4d1d197eb Mon Sep 17 00:00:00 2001 From: shuai Date: Tue, 27 Aug 2024 16:15:10 +0800 Subject: [PATCH 50/64] fix: badges link url --- ui/src/pages/Badges/Detail/index.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ui/src/pages/Badges/Detail/index.tsx b/ui/src/pages/Badges/Detail/index.tsx index f8a9327af..27fe9b7ed 100644 --- a/ui/src/pages/Badges/Detail/index.tsx +++ b/ui/src/pages/Badges/Detail/index.tsx @@ -71,13 +71,13 @@ const Index = () => { badges?.list?.map((item) => { const linkUrl = item.object_type === 'question' - ? `/question/${item.question_id}` + ? `/questions/${item.question_id}` : item.object_type === 'answer' - ? `/question/${item.question_id}/${item.answer_id}` + ? `/questions/${item.question_id}/${item.answer_id}` : item.object_type === 'comment' && item.answer_id - ? `/question/${item.question_id}/${item.answer_id}?commentId=${item.comment_id}` + ? `/questions/${item.question_id}/${item.answer_id}?commentId=${item.comment_id}` : item.object_type === 'comment' - ? `/question/${item.question_id}?commentId=${item.comment_id}` + ? `/questions/${item.question_id}?commentId=${item.comment_id}` : ''; return ( Date: Fri, 30 Aug 2024 15:28:21 +0800 Subject: [PATCH 51/64] refactor: Remove unnecessary CSS property in Editor utils --- ui/src/components/Editor/utils/index.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/ui/src/components/Editor/utils/index.ts b/ui/src/components/Editor/utils/index.ts index 0624418a8..759033cca 100644 --- a/ui/src/components/Editor/utils/index.ts +++ b/ui/src/components/Editor/utils/index.ts @@ -98,7 +98,6 @@ export const useEditor = ({ '.cm-line': { whiteSpace: 'pre-wrap', wordWrap: 'break-word', - wordBreak: 'break-all', }, '.ͼ7, .ͼ6': { textDecoration: 'none', From 23323d9346e6189a36621b32a7792b5932352964 Mon Sep 17 00:00:00 2001 From: shuai Date: Fri, 30 Aug 2024 15:55:56 +0800 Subject: [PATCH 52/64] fix: When using base_url, some links are missing the base path --- ui/src/components/AccordionNav/index.tsx | 70 +++++++++++++------ .../Header/components/NavItems/index.tsx | 48 +++++++------ ui/src/components/QueryGroup/index.tsx | 8 ++- ui/src/components/SideNav/index.tsx | 18 ++--- 4 files changed, 86 insertions(+), 58 deletions(-) diff --git a/ui/src/components/AccordionNav/index.tsx b/ui/src/components/AccordionNav/index.tsx index 91c4d1c57..a0e666e17 100644 --- a/ui/src/components/AccordionNav/index.tsx +++ b/ui/src/components/AccordionNav/index.tsx @@ -20,7 +20,7 @@ import React, { FC, useEffect, useState } from 'react'; import { Accordion, Nav } from 'react-bootstrap'; import { useTranslation } from 'react-i18next'; -import { useNavigate, useMatch } from 'react-router-dom'; +import { useNavigate, useMatch, NavLink } from 'react-router-dom'; import classNames from 'classnames'; @@ -38,30 +38,54 @@ function MenuNode({ const { t } = useTranslation('translation', { keyPrefix: 'nav_menus' }); const isLeaf = !menu.children.length; const href = isLeaf ? `${path}${menu.path}` : '#'; - return ( - { - callback(evt, menu, href, isLeaf); - }} - href={href} - className={classNames( - 'text-nowrap d-flex flex-nowrap align-items-center w-100', - { expanding, 'link-dark': activeKey !== menu.path }, - )}> - - {menu.displayName ? menu.displayName : t(menu.name)} - - {menu.badgeContent ? ( - {menu.badgeContent} - ) : null} - {!isLeaf && ( - - )} - + {isLeaf ? ( + { + callback(evt, menu, href, isLeaf); + }} + className={classNames( + 'text-nowrap d-flex flex-nowrap align-items-center w-100', + { expanding, 'link-dark': activeKey !== menu.path }, + )}> + + {menu.displayName ? menu.displayName : t(menu.name)} + + {menu.badgeContent ? ( + {menu.badgeContent} + ) : null} + {!isLeaf && ( + + )} + + ) : ( + { + callback(evt, menu, href, isLeaf); + }} + className={classNames( + 'text-nowrap d-flex flex-nowrap align-items-center w-100', + { expanding, 'link-dark': activeKey !== menu.path }, + )}> + + {menu.displayName ? menu.displayName : t(menu.name)} + + {menu.badgeContent ? ( + {menu.badgeContent} + ) : null} + {!isLeaf && ( + + )} + + )} + {menu.children.length ? ( <> diff --git a/ui/src/components/Header/components/NavItems/index.tsx b/ui/src/components/Header/components/NavItems/index.tsx index f3368b0f5..bd80729d3 100644 --- a/ui/src/components/Header/components/NavItems/index.tsx +++ b/ui/src/components/Header/components/NavItems/index.tsx @@ -35,24 +35,25 @@ interface Props { const Index: FC = ({ redDot, userInfo, logOut }) => { const { t } = useTranslation(); - // eslint-disable-next-line @typescript-eslint/no-unused-vars const navigate = useNavigate(); + // eslint-disable-next-line @typescript-eslint/no-unused-vars const { agent: ucAgent } = userCenterStore(); const handleLinkClick = (evt) => { if (floppyNavigation.shouldProcessLinkClick(evt)) { evt.preventDefault(); const href = evt.currentTarget.getAttribute('href'); - navigate(href); + floppyNavigation.navigate(href, { + handler: navigate, + }); } }; return ( <> @@ -95,25 +95,31 @@ const Index: FC = ({ redDot, userInfo, logOut }) => { - 'dropdown-item'} onClick={handleLinkClick}> {t('header.nav.profile')} - - + 'dropdown-item'} onClick={handleLinkClick}> {t('header.nav.bookmark')} - - + 'dropdown-item'} onClick={handleLinkClick}> {t('header.nav.setting')} - + - logOut(e)}> + 'dropdown-item'} + onClick={(e) => logOut(e)}> {t('header.nav.logout')} - + {/* Dropdown for user center agent info */} diff --git a/ui/src/components/QueryGroup/index.tsx b/ui/src/components/QueryGroup/index.tsx index a201ae7aa..9b37c6e9c 100644 --- a/ui/src/components/QueryGroup/index.tsx +++ b/ui/src/components/QueryGroup/index.tsx @@ -24,6 +24,7 @@ import { useTranslation } from 'react-i18next'; import classNames from 'classnames'; +import { REACT_BASE_PATH } from '@/router/alias'; import { floppyNavigation } from '@/utils'; interface Props { @@ -82,7 +83,6 @@ const Index: FC = ({ const name = typeof btn === 'string' ? btn : btn.name; return (
From 90faa87e52bfee67d87e86a2426232efb906b1aa Mon Sep 17 00:00:00 2001 From: LinkinStars Date: Wed, 28 Aug 2024 15:34:40 +0800 Subject: [PATCH 54/64] refactor(badge): remove unused code --- internal/service/badge/badge_award_service.go | 7 ------- 1 file changed, 7 deletions(-) diff --git a/internal/service/badge/badge_award_service.go b/internal/service/badge/badge_award_service.go index 02cdcf1c8..0bc7be0fe 100644 --- a/internal/service/badge/badge_award_service.go +++ b/internal/service/badge/badge_award_service.go @@ -273,13 +273,6 @@ func (bs *BadgeAwardService) GetUserRecentBadgeAwardList(ctx *gin.Context, req * return } -// validate user - -type userReq struct { - UserID string - Username string -} - func (bs *BadgeAwardService) validateUserByUsername(ctx context.Context, userName string) (userID string, err error) { var ( userInfo *schema.UserBasicInfo From 6aa64092b625b9e8f0edb503207bd88a77335ddf Mon Sep 17 00:00:00 2001 From: kumfo Date: Mon, 2 Sep 2024 15:32:03 +0800 Subject: [PATCH 55/64] feat(badge): feat badge description url --- i18n/en_US.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/i18n/en_US.yaml b/i18n/en_US.yaml index 032ad1417..2b0a47144 100644 --- a/i18n/en_US.yaml +++ b/i18n/en_US.yaml @@ -539,7 +539,7 @@ backend: name: other: Autobiographer desc: - other: Filled out [profile] information. + other: Filled out profile information. certified: name: other: Certified From 4f80f9e97b02a7329d2c24beefe869c8a8d571aa Mon Sep 17 00:00:00 2001 From: kumfo Date: Mon, 2 Sep 2024 18:12:04 +0800 Subject: [PATCH 56/64] feat(badge): badge list earned return earned total --- docs/docs.go | 6 +++--- docs/swagger.json | 6 +++--- docs/swagger.yaml | 6 +++--- internal/schema/badge_schema.go | 2 +- internal/service/badge/badge_service.go | 4 ++-- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/docs/docs.go b/docs/docs.go index 389f3bdfa..e5cc6f166 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -7795,7 +7795,7 @@ const docTemplate = `{ }, "earned": { "description": "badge earned count", - "type": "boolean" + "type": "integer" }, "icon": { "description": "badge icon", @@ -11472,8 +11472,8 @@ var SwaggerInfo = &swag.Spec{ Host: "", BasePath: "= \"/\"", Schemes: []string{}, - Title: "\"answer\"", - Description: "= \"answer api\"", + Title: "\"apache answer\"", + Description: "= \"apache answer api\"", InfoInstanceName: "swagger", SwaggerTemplate: docTemplate, LeftDelim: "{{", diff --git a/docs/swagger.json b/docs/swagger.json index 3d6f07c8d..5fd369073 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -1,8 +1,8 @@ { "swagger": "2.0", "info": { - "description": "= \"answer api\"", - "title": "\"answer\"", + "description": "= \"apache answer api\"", + "title": "\"apache answer\"", "contact": {}, "version": "= \"v0.0.1\"" }, @@ -7769,7 +7769,7 @@ }, "earned": { "description": "badge earned count", - "type": "boolean" + "type": "integer" }, "icon": { "description": "badge icon", diff --git a/docs/swagger.yaml b/docs/swagger.yaml index d2d435041..72871edcd 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -372,7 +372,7 @@ definitions: type: integer earned: description: badge earned count - type: boolean + type: integer icon: description: badge icon type: string @@ -2909,8 +2909,8 @@ definitions: type: object info: contact: {} - description: = "answer api" - title: '"answer"' + description: = "apache answer api" + title: '"apache answer"' version: = "v0.0.1" paths: /: diff --git a/internal/schema/badge_schema.go b/internal/schema/badge_schema.go index efbcd37ad..1dd2e76f0 100644 --- a/internal/schema/badge_schema.go +++ b/internal/schema/badge_schema.go @@ -49,7 +49,7 @@ type BadgeListInfo struct { // badge award count AwardCount int `json:"award_count" ` // badge earned count - Earned bool `json:"earned" ` + Earned int64 `json:"earned" ` // badge level Level entity.BadgeLevel `json:"level" ` } diff --git a/internal/service/badge/badge_service.go b/internal/service/badge/badge_service.go index 031f0f152..49591adc8 100644 --- a/internal/service/badge/badge_service.go +++ b/internal/service/badge/badge_service.go @@ -99,11 +99,11 @@ func (b *BadgeService) ListByGroup(ctx context.Context, userID string) (resp []* for _, badge := range badges { // check is earned - earned := false + var earned int64 = 0 if len(earnedCounts) > 0 { for _, earnedCount := range earnedCounts { if badge.ID == earnedCount.BadgeID && earnedCount.EarnedCount > 0 { - earned = true + earned = earnedCount.EarnedCount break } } From 1666b9413514fc19bc24e6629927589058ea46d8 Mon Sep 17 00:00:00 2001 From: kumfo Date: Mon, 2 Sep 2024 18:26:24 +0800 Subject: [PATCH 57/64] feat(badge): badge list earned return earned total --- docs/docs.go | 2 +- docs/swagger.json | 2 +- docs/swagger.yaml | 2 +- internal/schema/badge_schema.go | 2 +- internal/service/badge/badge_service.go | 12 ++++++------ 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/docs/docs.go b/docs/docs.go index e5cc6f166..a9d56ab97 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -7793,7 +7793,7 @@ const docTemplate = `{ "description": "badge award count", "type": "integer" }, - "earned": { + "earned_count": { "description": "badge earned count", "type": "integer" }, diff --git a/docs/swagger.json b/docs/swagger.json index 5fd369073..82ec1a6ff 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -7767,7 +7767,7 @@ "description": "badge award count", "type": "integer" }, - "earned": { + "earned_count": { "description": "badge earned count", "type": "integer" }, diff --git a/docs/swagger.yaml b/docs/swagger.yaml index 72871edcd..2af2900fb 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -370,7 +370,7 @@ definitions: award_count: description: badge award count type: integer - earned: + earned_count: description: badge earned count type: integer icon: diff --git a/internal/schema/badge_schema.go b/internal/schema/badge_schema.go index 1dd2e76f0..a33fb480d 100644 --- a/internal/schema/badge_schema.go +++ b/internal/schema/badge_schema.go @@ -49,7 +49,7 @@ type BadgeListInfo struct { // badge award count AwardCount int `json:"award_count" ` // badge earned count - Earned int64 `json:"earned" ` + EarnedCount int64 `json:"earned_count" ` // badge level Level entity.BadgeLevel `json:"level" ` } diff --git a/internal/service/badge/badge_service.go b/internal/service/badge/badge_service.go index 49591adc8..3ec7b5bfb 100644 --- a/internal/service/badge/badge_service.go +++ b/internal/service/badge/badge_service.go @@ -110,12 +110,12 @@ func (b *BadgeService) ListByGroup(ctx context.Context, userID string) (resp []* } badgesMap[badge.BadgeGroupID] = append(badgesMap[badge.BadgeGroupID], &schema.BadgeListInfo{ - ID: uid.EnShortID(badge.ID), - Name: translator.Tr(handler.GetLangByCtx(ctx), badge.Name), - Icon: badge.Icon, - AwardCount: badge.AwardCount, - Earned: earned, - Level: badge.Level, + ID: uid.EnShortID(badge.ID), + Name: translator.Tr(handler.GetLangByCtx(ctx), badge.Name), + Icon: badge.Icon, + AwardCount: badge.AwardCount, + EarnedCount: earned, + Level: badge.Level, }) } From 8b9f5d4448f9e4a4498631d5c6c5131f4da557d4 Mon Sep 17 00:00:00 2001 From: robin Date: Mon, 2 Sep 2024 16:35:33 +0800 Subject: [PATCH 58/64] refactor: Add links to badge names and award counts in Admin Badges page --- ui/src/pages/Admin/Badges/index.tsx | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/ui/src/pages/Admin/Badges/index.tsx b/ui/src/pages/Admin/Badges/index.tsx index e76f90eba..4cadc1c3d 100644 --- a/ui/src/pages/Admin/Badges/index.tsx +++ b/ui/src/pages/Admin/Badges/index.tsx @@ -19,7 +19,7 @@ import { FC } from 'react'; import { Form, Table, Stack } from 'react-bootstrap'; -import { useSearchParams } from 'react-router-dom'; +import { Link, useSearchParams } from 'react-router-dom'; import { useTranslation } from 'react-i18next'; import classNames from 'classnames'; @@ -126,13 +126,15 @@ const Badges: FC = () => { /> )}
-
{badge.name}
+ {badge.name}
{badge.description}
{badge.group_name} - {badge.award_count} + + {badge.award_count} + {t(badge.status)} From afaf11ffb64af31836d4d6e6f59ca2a7fb977420 Mon Sep 17 00:00:00 2001 From: robin Date: Mon, 2 Sep 2024 16:59:28 +0800 Subject: [PATCH 59/64] refactor: Update badge labels to use multiplication symbol instead of 'x' --- i18n/en_US.yaml | 4 ++-- ui/src/components/CardBadge/index.tsx | 4 ++-- ui/src/pages/Badges/Detail/components/Badge/index.tsx | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/i18n/en_US.yaml b/i18n/en_US.yaml index 2b0a47144..f7c16e641 100644 --- a/i18n/en_US.yaml +++ b/i18n/en_US.yaml @@ -1748,8 +1748,8 @@ ui: confirm: View badges title: Badges awarded: Awarded - earned_x: Earned x{{ number }} - x_awarded: "{{ number }} awarded" + earned_×: Earned ×{{ number }} + ×_awarded: "{{ number }} awarded" can_earn_multiple: You can earn this multiple times. earned: Earned diff --git a/ui/src/components/CardBadge/index.tsx b/ui/src/components/CardBadge/index.tsx index dd8c18e61..fcb1bb9db 100644 --- a/ui/src/components/CardBadge/index.tsx +++ b/ui/src/components/CardBadge/index.tsx @@ -57,7 +57,7 @@ const Index: FC = ({ {badgePillType === 'count' && Number(data?.earned_count) > 0 && ( - x{data.earned_count} + ×{data.earned_count} )} {data.icon.startsWith('http') ? ( @@ -78,7 +78,7 @@ const Index: FC = ({
{data.name}
{showAwardedCount && (
- {t('x_awarded', { number: formatCount(data.award_count) })} + {t('×_awarded', { number: formatCount(data.award_count) })}
)} diff --git a/ui/src/pages/Badges/Detail/components/Badge/index.tsx b/ui/src/pages/Badges/Detail/components/Badge/index.tsx index affb73e6e..29da1af6a 100644 --- a/ui/src/pages/Badges/Detail/components/Badge/index.tsx +++ b/ui/src/pages/Badges/Detail/components/Badge/index.tsx @@ -73,13 +73,13 @@ const Index: FC = ({ data }) => {
{data.award_count > 0 && ( - {t('x_awarded', { number: formatCount(data.award_count) })} + {t('×_awarded', { number: formatCount(data.award_count) })} )} {data.earned_count > 0 && ( - {t('earned_x', { number: data.earned_count })} + {t('earned_×', { number: data.earned_count })} )}
From ca8c4b40c653858dbcd3ad70e6c1790577bf755d Mon Sep 17 00:00:00 2001 From: robin Date: Tue, 3 Sep 2024 11:05:03 +0800 Subject: [PATCH 60/64] refactor: Update badge labels to use multiplication symbol instead of 'x' --- ui/src/components/CardBadge/index.tsx | 10 ++++++---- ui/src/pages/Badges/Detail/components/Badge/index.tsx | 5 ++++- .../pages/Users/Personal/components/NavBar/index.tsx | 1 - 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/ui/src/components/CardBadge/index.tsx b/ui/src/components/CardBadge/index.tsx index fcb1bb9db..a2a0bd7da 100644 --- a/ui/src/components/CardBadge/index.tsx +++ b/ui/src/components/CardBadge/index.tsx @@ -49,14 +49,16 @@ const Index: FC = ({ className="card text-center badge-card" to={`/badges/${data.id}${urlSearchParams ? `?${urlSearchParams}` : ''}`}> - {data.earned && badgePillType === 'earned' && ( + {Number(data?.earned_count) > 0 && badgePillType === 'earned' && ( - {t('earned')} + {`${t('earned')}${ + Number(data?.earned_count) > 1 ? ` ×${data.earned_count}` : '' + }`} )} - {badgePillType === 'count' && Number(data?.earned_count) > 0 && ( - + {badgePillType === 'count' && Number(data?.earned_count) > 1 && ( + ×{data.earned_count} )} diff --git a/ui/src/pages/Badges/Detail/components/Badge/index.tsx b/ui/src/pages/Badges/Detail/components/Badge/index.tsx index 29da1af6a..a554a6bca 100644 --- a/ui/src/pages/Badges/Detail/components/Badge/index.tsx +++ b/ui/src/pages/Badges/Detail/components/Badge/index.tsx @@ -77,11 +77,14 @@ const Index: FC = ({ data }) => {
)} - {data.earned_count > 0 && ( + {data.earned_count > 1 && ( {t('earned_×', { number: data.earned_count })} )} + {data.earned_count === 1 && ( + {t('earned')} + )}
)}
diff --git a/ui/src/pages/Users/Personal/components/NavBar/index.tsx b/ui/src/pages/Users/Personal/components/NavBar/index.tsx index 5f4360ed8..9c6d157a4 100644 --- a/ui/src/pages/Users/Personal/components/NavBar/index.tsx +++ b/ui/src/pages/Users/Personal/components/NavBar/index.tsx @@ -60,7 +60,6 @@ const list = [ name: 'votes', }, { - role: 'self', // Only visible to author path: '/badges', name: 'badges', }, From d72cf56b8c4d2827977547cbc3f19fed9b1401f0 Mon Sep 17 00:00:00 2001 From: robin Date: Tue, 3 Sep 2024 11:58:57 +0800 Subject: [PATCH 61/64] refactor: Add text truncation to TopList component link --- ui/src/pages/Users/Personal/components/TopList/index.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/ui/src/pages/Users/Personal/components/TopList/index.tsx b/ui/src/pages/Users/Personal/components/TopList/index.tsx index 045534288..aa896caf1 100644 --- a/ui/src/pages/Users/Personal/components/TopList/index.tsx +++ b/ui/src/pages/Users/Personal/components/TopList/index.tsx @@ -38,6 +38,7 @@ const Index: FC = ({ data, type }) => { className={`${index === data.length - 1 ? '' : 'mb-2'}`} key={type === 'answer' ? item.answer_id : item.question_id}> Date: Tue, 3 Sep 2024 12:28:01 +0800 Subject: [PATCH 62/64] refactor: Update badge description rendering in Admin Badges page --- ui/src/pages/Admin/Badges/index.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/ui/src/pages/Admin/Badges/index.tsx b/ui/src/pages/Admin/Badges/index.tsx index 4cadc1c3d..29661714c 100644 --- a/ui/src/pages/Admin/Badges/index.tsx +++ b/ui/src/pages/Admin/Badges/index.tsx @@ -127,7 +127,12 @@ const Badges: FC = () => { )}
{badge.name} -
{badge.description}
+
From 8a31d29ce7ddea8a256f92dbf2cef2b0da20413f Mon Sep 17 00:00:00 2001 From: lhpqaq <657407891@qq.com> Date: Tue, 3 Sep 2024 12:48:47 +0800 Subject: [PATCH 63/64] Fix ListNewestEarned in high version MySQL --- internal/repo/badge_award/badge_award_repo.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/repo/badge_award/badge_award_repo.go b/internal/repo/badge_award/badge_award_repo.go index 377677d19..3a04fd22b 100644 --- a/internal/repo/badge_award/badge_award_repo.go +++ b/internal/repo/badge_award/badge_award_repo.go @@ -139,7 +139,7 @@ func (r *badgeAwardRepo) ListPagedByBadgeIdAndUserId(ctx context.Context, badgeI func (r *badgeAwardRepo) ListNewestEarned(ctx context.Context, userID string, limit int) (badgeAwards []*entity.BadgeAwardRecent, err error) { badgeAwards = make([]*entity.BadgeAwardRecent, 0) err = r.data.DB.Context(ctx). - Select("user_id, badge_id, max(created_at) created,count(*) earned_count"). + Select("any_value(user_id), badge_id, max(created_at) created,count(*) earned_count"). Where("user_id = ? AND is_badge_deleted = ? ", userID, entity.IsBadgeNotDeleted). GroupBy("badge_id"). OrderBy("created desc"). From 70acbe38e053863e02ab033ea7b896a7636f0352 Mon Sep 17 00:00:00 2001 From: shuai Date: Tue, 3 Sep 2024 14:34:58 +0800 Subject: [PATCH 64/64] fix: styles --- ui/src/components/CardBadge/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/src/components/CardBadge/index.tsx b/ui/src/components/CardBadge/index.tsx index a2a0bd7da..214e3f5d0 100644 --- a/ui/src/components/CardBadge/index.tsx +++ b/ui/src/components/CardBadge/index.tsx @@ -79,7 +79,7 @@ const Index: FC = ({
{data.name}
{showAwardedCount && ( -
+
{t('×_awarded', { number: formatCount(data.award_count) })}
)}