added the ability to remove achievements and changed the process of deleting achievements and types
This commit is contained in:
parent
0d582ec803
commit
df25e1f6fc
@ -2,6 +2,7 @@ package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net/http"
|
||||
"runtime/debug"
|
||||
"strings"
|
||||
@ -81,6 +82,12 @@ type SubscriptionAPIRequest struct {
|
||||
ChannelID string `json:"channel_id"`
|
||||
}
|
||||
|
||||
type RevokeOwnershipRequest struct {
|
||||
BadgeID string `json:"badge_id"`
|
||||
UserID string `json:"user_id"`
|
||||
Time string `json:"time"`
|
||||
}
|
||||
|
||||
type TypeWithBadgeCount struct {
|
||||
*badgesmodel.BadgeTypeDefinition
|
||||
BadgeCount int `json:"badge_count"`
|
||||
@ -114,6 +121,7 @@ func (p *Plugin) initializeAPI() {
|
||||
apiRouter.HandleFunc("/deleteBadge/{badgeID}", p.extractUserMiddleWare(p.apiDeleteBadge, ResponseTypeJSON)).Methods(http.MethodDelete)
|
||||
apiRouter.HandleFunc("/deleteType/{typeID}", p.extractUserMiddleWare(p.apiDeleteType, ResponseTypeJSON)).Methods(http.MethodDelete)
|
||||
apiRouter.HandleFunc("/grantBadge", p.extractUserMiddleWare(p.apiGrantBadge, ResponseTypeJSON)).Methods(http.MethodPost)
|
||||
apiRouter.HandleFunc("/revokeOwnership", p.extractUserMiddleWare(p.apiRevokeOwnership, ResponseTypeJSON)).Methods(http.MethodPost)
|
||||
apiRouter.HandleFunc("/getChannelSubscriptions/{channelID}", p.extractUserMiddleWare(p.apiGetChannelSubscriptions, ResponseTypeJSON)).Methods(http.MethodGet)
|
||||
apiRouter.HandleFunc("/createSubscription", p.extractUserMiddleWare(p.apiCreateSubscription, ResponseTypeJSON)).Methods(http.MethodPost)
|
||||
apiRouter.HandleFunc("/deleteSubscription", p.extractUserMiddleWare(p.apiDeleteSubscription, ResponseTypeJSON)).Methods(http.MethodPost)
|
||||
@ -1123,6 +1131,10 @@ func (p *Plugin) dialogGrant(w http.ResponseWriter, r *http.Request, userID stri
|
||||
reason, _ := req.Submission[DialogFieldGrantReason].(string)
|
||||
|
||||
shouldNotify, err := p.store.GrantBadge(badgesmodel.BadgeID(badgeIDStr), grantToID, userID, reason)
|
||||
if err == errAlreadyOwned {
|
||||
dialogError(w, T("badges.error.already_owned", "Это достижение уже выдано этому пользователю"), nil)
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
p.writeAPIError(w, &APIErrorResponse{
|
||||
ID: "cannot grant badge",
|
||||
@ -1302,6 +1314,12 @@ func (p *Plugin) grantBadge(w http.ResponseWriter, r *http.Request, pluginID str
|
||||
}
|
||||
|
||||
shouldNotify, err := p.store.GrantBadge(req.BadgeID, req.UserID, req.BotID, req.Reason)
|
||||
if err == errAlreadyOwned {
|
||||
p.writeAPIError(w, &APIErrorResponse{
|
||||
ID: "already_owned", Message: "This badge is already owned by this user", StatusCode: http.StatusConflict,
|
||||
})
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
p.writeAPIError(w, &APIErrorResponse{
|
||||
ID: "cannot grant badge",
|
||||
@ -1670,6 +1688,12 @@ func (p *Plugin) apiGrantBadge(w http.ResponseWriter, r *http.Request, userID st
|
||||
}
|
||||
|
||||
shouldNotify, err := p.store.GrantBadge(badgesmodel.BadgeID(req.BadgeID), req.UserID, userID, req.Reason)
|
||||
if errors.Is(err, errAlreadyOwned) {
|
||||
p.writeAPIError(w, &APIErrorResponse{
|
||||
ID: "already_owned", Message: "This badge is already owned by this user", StatusCode: http.StatusConflict,
|
||||
})
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
p.writeAPIError(w, &APIErrorResponse{
|
||||
ID: "cannot_grant_badge", Message: err.Error(), StatusCode: http.StatusInternalServerError,
|
||||
@ -1811,3 +1835,50 @@ func (p *Plugin) apiGetChannelSubscriptions(w http.ResponseWriter, r *http.Reque
|
||||
b, _ := json.Marshal(types)
|
||||
_, _ = w.Write(b)
|
||||
}
|
||||
|
||||
func (p *Plugin) apiRevokeOwnership(w http.ResponseWriter, r *http.Request, userID string) {
|
||||
var req RevokeOwnershipRequest
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
p.writeAPIError(w, &APIErrorResponse{
|
||||
ID: "invalid_request", Message: "Invalid request body", StatusCode: http.StatusBadRequest,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
req.BadgeID = strings.TrimSpace(req.BadgeID)
|
||||
req.UserID = strings.TrimSpace(req.UserID)
|
||||
req.Time = strings.TrimSpace(req.Time)
|
||||
if req.BadgeID == "" || req.UserID == "" || req.Time == "" {
|
||||
p.writeAPIError(w, &APIErrorResponse{
|
||||
ID: "invalid_request", Message: "badge_id, user_id and time are required", StatusCode: http.StatusBadRequest,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
ownership, err := p.store.FindOwnership(badgesmodel.BadgeID(req.BadgeID), req.UserID, req.Time)
|
||||
if err != nil {
|
||||
p.writeAPIError(w, &APIErrorResponse{
|
||||
ID: "ownership_not_found", Message: "Ownership not found", StatusCode: http.StatusNotFound,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
isAdmin := p.badgeAdminUserIDs[userID]
|
||||
if ownership.GrantedBy != userID && !isAdmin {
|
||||
p.writeAPIError(w, &APIErrorResponse{
|
||||
ID: "no_permission_revoke", Message: "No permission to revoke this ownership", StatusCode: http.StatusForbidden,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if err := p.store.RevokeOwnership(badgesmodel.BadgeID(req.BadgeID), req.UserID, req.Time); err != nil {
|
||||
p.writeAPIError(w, &APIErrorResponse{
|
||||
ID: "cannot_revoke", Message: err.Error(), StatusCode: http.StatusInternalServerError,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
resp := map[string]string{"status": "ok"}
|
||||
b, _ := json.Marshal(resp)
|
||||
_, _ = w.Write(b)
|
||||
}
|
||||
|
||||
@ -592,6 +592,9 @@ func (p *Plugin) runGrant(args []string, extra *model.CommandArgs) (bool, *model
|
||||
}
|
||||
|
||||
shouldNotify, err := p.store.GrantBadge(badgesmodel.BadgeID(badgeStr), user.Id, extra.UserId, "")
|
||||
if err == errAlreadyOwned {
|
||||
return commandError(T("badges.error.already_owned", "Это достижение уже выдано этому пользователю"))
|
||||
}
|
||||
if err != nil {
|
||||
return commandError(err.Error())
|
||||
}
|
||||
|
||||
@ -1,21 +1,21 @@
|
||||
[
|
||||
{"id": "badges.dialog.create_badge.title", "translation": "Create badge"},
|
||||
{"id": "badges.dialog.create_badge.title", "translation": "Create achievement"},
|
||||
{"id": "badges.dialog.create_badge.submit", "translation": "Create"},
|
||||
{"id": "badges.dialog.edit_badge.title", "translation": "Edit badge"},
|
||||
{"id": "badges.dialog.edit_badge.title", "translation": "Edit achievement"},
|
||||
{"id": "badges.dialog.edit_badge.submit", "translation": "Save"},
|
||||
{"id": "badges.dialog.create_type.title", "translation": "Create type"},
|
||||
{"id": "badges.dialog.create_type.submit", "translation": "Create"},
|
||||
{"id": "badges.dialog.edit_type.title", "translation": "Edit type"},
|
||||
{"id": "badges.dialog.edit_type.submit", "translation": "Save"},
|
||||
{"id": "badges.dialog.grant.title", "translation": "Grant badge"},
|
||||
{"id": "badges.dialog.grant.title", "translation": "Grant achievement"},
|
||||
{"id": "badges.dialog.grant.submit", "translation": "Grant"},
|
||||
{"id": "badges.dialog.grant.intro", "translation": "Grant badge to @%s"},
|
||||
{"id": "badges.dialog.grant.intro", "translation": "Grant achievement to @%s"},
|
||||
{"id": "badges.dialog.create_subscription.title", "translation": "Create subscription"},
|
||||
{"id": "badges.dialog.create_subscription.submit", "translation": "Add"},
|
||||
{"id": "badges.dialog.create_subscription.intro", "translation": "Select the badge type you want to subscribe to this channel."},
|
||||
{"id": "badges.dialog.create_subscription.intro", "translation": "Select the achievement type you want to subscribe to this channel."},
|
||||
{"id": "badges.dialog.delete_subscription.title", "translation": "Delete subscription"},
|
||||
{"id": "badges.dialog.delete_subscription.submit", "translation": "Remove"},
|
||||
{"id": "badges.dialog.delete_subscription.intro", "translation": "Select the badge type you want to unsubscribe from this channel."},
|
||||
{"id": "badges.dialog.delete_subscription.intro", "translation": "Select the achievement type you want to unsubscribe from this channel."},
|
||||
|
||||
{"id": "badges.field.name", "translation": "Name"},
|
||||
{"id": "badges.field.description", "translation": "Description"},
|
||||
@ -23,45 +23,46 @@
|
||||
{"id": "badges.field.image.help", "translation": "Enter an emoticon name"},
|
||||
{"id": "badges.field.type", "translation": "Type"},
|
||||
{"id": "badges.field.multiple", "translation": "Multiple"},
|
||||
{"id": "badges.field.multiple.help", "translation": "Whether the badge can be granted multiple times"},
|
||||
{"id": "badges.field.delete_badge", "translation": "Delete badge"},
|
||||
{"id": "badges.field.delete_badge.help", "translation": "WARNING: checking this will remove this badge permanently."},
|
||||
{"id": "badges.field.everyone_can_create", "translation": "Everyone can create badge"},
|
||||
{"id": "badges.field.everyone_can_create.help", "translation": "Whether any user can create a badge of this type"},
|
||||
{"id": "badges.field.multiple.help", "translation": "Whether the achievement can be granted multiple times"},
|
||||
{"id": "badges.field.delete_badge", "translation": "Delete achievement"},
|
||||
{"id": "badges.field.delete_badge.help", "translation": "WARNING: checking this will remove this achievement permanently."},
|
||||
{"id": "badges.field.everyone_can_create", "translation": "Everyone can create achievement"},
|
||||
{"id": "badges.field.everyone_can_create.help", "translation": "Whether any user can create an achievement of this type"},
|
||||
{"id": "badges.field.allowlist_create", "translation": "Can create allowlist"},
|
||||
{"id": "badges.field.allowlist_create.help", "translation": "Fill the usernames separated by comma (,) of the people that can create badges of this type."},
|
||||
{"id": "badges.field.everyone_can_grant", "translation": "Everyone can grant badge"},
|
||||
{"id": "badges.field.everyone_can_grant.help", "translation": "Whether any user can grant a badge of this type"},
|
||||
{"id": "badges.field.allowlist_create.help", "translation": "Fill the usernames separated by comma (,) of the people that can create achievements of this type."},
|
||||
{"id": "badges.field.everyone_can_grant", "translation": "Everyone can grant achievement"},
|
||||
{"id": "badges.field.everyone_can_grant.help", "translation": "Whether any user can grant an achievement of this type"},
|
||||
{"id": "badges.field.allowlist_grant", "translation": "Can grant allowlist"},
|
||||
{"id": "badges.field.allowlist_grant.help", "translation": "Fill the usernames separated by comma (,) of the people that can grant badges of this type."},
|
||||
{"id": "badges.field.allowlist_grant.help", "translation": "Fill the usernames separated by comma (,) of the people that can grant achievements of this type."},
|
||||
{"id": "badges.field.delete_type", "translation": "Remove type"},
|
||||
{"id": "badges.field.delete_type.help", "translation": "WARNING: checking this will remove this type and all associated badges permanently."},
|
||||
{"id": "badges.field.delete_type.help", "translation": "WARNING: checking this will remove this type and all associated achievements permanently."},
|
||||
{"id": "badges.field.user", "translation": "User"},
|
||||
{"id": "badges.field.badge", "translation": "Badge"},
|
||||
{"id": "badges.field.badge", "translation": "Achievement"},
|
||||
{"id": "badges.field.reason", "translation": "Reason"},
|
||||
{"id": "badges.field.reason.help", "translation": "Reason why you are granting this badge. This will be seen by the user, and wherever this grant notification is shown (e.g. subscriptions)."},
|
||||
{"id": "badges.field.reason.help", "translation": "Reason why you are granting this achievement. This will be seen by the user, and wherever this grant notification is shown (e.g. subscriptions)."},
|
||||
{"id": "badges.field.notify_here", "translation": "Notify on this channel"},
|
||||
{"id": "badges.field.notify_here.help", "translation": "If you mark this, the bot will send a message to this channel notifying that you granted this badge to this person."},
|
||||
{"id": "badges.field.notify_here.help", "translation": "If you mark this, the bot will send a message to this channel notifying that you granted this achievement to this person."},
|
||||
|
||||
{"id": "badges.error.unknown", "translation": "An unknown error occurred. Please talk to your system administrator for help."},
|
||||
{"id": "badges.error.cannot_get_user", "translation": "Cannot get user."},
|
||||
{"id": "badges.error.only_sysadmin_clean", "translation": "Only a system admin can clean the badges database."},
|
||||
{"id": "badges.error.only_sysadmin_clean", "translation": "Only a system admin can clean the achievements database."},
|
||||
{"id": "badges.error.specify_create", "translation": "Specify what you want to create."},
|
||||
{"id": "badges.error.create_badge_or_type", "translation": "You can create either badge or type"},
|
||||
{"id": "badges.error.no_types_available", "translation": "You cannot create badges from any type."},
|
||||
{"id": "badges.error.must_set_badge_id", "translation": "You must set the badge ID"},
|
||||
{"id": "badges.error.cannot_edit_badge", "translation": "You cannot edit this badge"},
|
||||
{"id": "badges.error.create_badge_or_type", "translation": "You can create either achievement or type"},
|
||||
{"id": "badges.error.no_types_available", "translation": "You cannot create achievements from any type."},
|
||||
{"id": "badges.error.must_set_badge_id", "translation": "You must set the achievement ID"},
|
||||
{"id": "badges.error.cannot_edit_badge", "translation": "You cannot edit this achievement"},
|
||||
{"id": "badges.error.specify_edit", "translation": "Specify what you want to edit."},
|
||||
{"id": "badges.error.edit_badge_or_type", "translation": "You can edit either badge or type"},
|
||||
{"id": "badges.error.no_permissions_edit_type", "translation": "You have no permissions to edit a badge type."},
|
||||
{"id": "badges.error.edit_badge_or_type", "translation": "You can edit either achievement or type"},
|
||||
{"id": "badges.error.no_permissions_edit_type", "translation": "You have no permissions to edit an achievement type."},
|
||||
{"id": "badges.error.must_provide_type_id", "translation": "You must provide a type id"},
|
||||
{"id": "badges.error.cannot_edit_type", "translation": "You cannot edit this type"},
|
||||
{"id": "badges.error.no_permissions_grant", "translation": "You have no permissions to grant this badge"},
|
||||
{"id": "badges.error.cannot_grant_badge", "translation": "You cannot grant that badge"},
|
||||
{"id": "badges.error.no_permissions_grant", "translation": "You have no permissions to grant this achievement"},
|
||||
{"id": "badges.error.cannot_grant_badge", "translation": "You cannot grant that achievement"},
|
||||
{"id": "badges.error.specify_subscription", "translation": "Specify what you want to do."},
|
||||
{"id": "badges.error.create_or_delete_subscription", "translation": "You can either create or delete subscriptions"},
|
||||
{"id": "badges.error.cannot_create_subscription", "translation": "You cannot create subscriptions"},
|
||||
{"id": "badges.error.no_permissions_create_type", "translation": "You have no permissions to create a badge type."},
|
||||
{"id": "badges.error.no_permissions_create_type", "translation": "You have no permissions to create an achievement type."},
|
||||
{"id": "badges.error.already_owned", "translation": "This achievement is already owned by this user"},
|
||||
|
||||
{"id": "badges.success.clean", "translation": "Clean"},
|
||||
{"id": "badges.success.granted", "translation": "Granted"},
|
||||
@ -72,8 +73,8 @@
|
||||
{"id": "badges.api.empty_emoji", "translation": "Empty emoji"},
|
||||
{"id": "badges.api.invalid_field", "translation": "Invalid field"},
|
||||
{"id": "badges.api.type_not_exist", "translation": "This type does not exist"},
|
||||
{"id": "badges.api.no_permissions_create_badge", "translation": "You have no permissions to create this badge"},
|
||||
{"id": "badges.api.badge_created", "translation": "Badge `%s` created."},
|
||||
{"id": "badges.api.no_permissions_create_badge", "translation": "You have no permissions to create this achievement"},
|
||||
{"id": "badges.api.badge_created", "translation": "Achievement `%s` created."},
|
||||
{"id": "badges.api.no_permissions_create_type", "translation": "You have no permissions to create a type"},
|
||||
{"id": "badges.api.cannot_find_user", "translation": "Cannot find user"},
|
||||
{"id": "badges.api.error_getting_user", "translation": "Error getting user %s: %v"},
|
||||
@ -83,15 +84,15 @@
|
||||
{"id": "badges.api.could_not_get_type", "translation": "Could not get the type"},
|
||||
{"id": "badges.api.no_permissions_edit_type", "translation": "You have no permissions to edit this type"},
|
||||
{"id": "badges.api.type_updated", "translation": "Type `%s` updated."},
|
||||
{"id": "badges.api.cannot_get_badge", "translation": "Cannot get badge"},
|
||||
{"id": "badges.api.cannot_edit_badge", "translation": "You cannot edit this badge"},
|
||||
{"id": "badges.api.could_not_get_badge", "translation": "Could not get the badge"},
|
||||
{"id": "badges.api.no_permissions_edit_badge", "translation": "You have no permissions to edit this badge"},
|
||||
{"id": "badges.api.badge_updated", "translation": "Badge `%s` updated."},
|
||||
{"id": "badges.api.badge_not_found", "translation": "Badge not found"},
|
||||
{"id": "badges.api.no_permissions_grant", "translation": "You have no permissions to grant this badge"},
|
||||
{"id": "badges.api.cannot_get_badge", "translation": "Cannot get achievement"},
|
||||
{"id": "badges.api.cannot_edit_badge", "translation": "You cannot edit this achievement"},
|
||||
{"id": "badges.api.could_not_get_badge", "translation": "Could not get the achievement"},
|
||||
{"id": "badges.api.no_permissions_edit_badge", "translation": "You have no permissions to edit this achievement"},
|
||||
{"id": "badges.api.badge_updated", "translation": "Achievement `%s` updated."},
|
||||
{"id": "badges.api.badge_not_found", "translation": "Achievement not found"},
|
||||
{"id": "badges.api.no_permissions_grant", "translation": "You have no permissions to grant this achievement"},
|
||||
{"id": "badges.api.user_not_found", "translation": "User not found"},
|
||||
{"id": "badges.api.badge_granted", "translation": "Badge `%s` granted to @%s."},
|
||||
{"id": "badges.api.badge_granted", "translation": "Achievement `%s` granted to @%s."},
|
||||
{"id": "badges.api.cannot_create_subscription", "translation": "You cannot create a subscription"},
|
||||
{"id": "badges.api.subscription_added", "translation": "Subscription added"},
|
||||
{"id": "badges.api.cannot_delete_subscription", "translation": "You cannot delete a subscription"},
|
||||
@ -99,9 +100,9 @@
|
||||
{"id": "badges.api.cannot_delete_default_type", "translation": "Cannot delete the default type"},
|
||||
{"id": "badges.api.not_authorized", "translation": "Not authorized"},
|
||||
|
||||
{"id": "badges.notify.dm_text", "translation": "@%s granted you the %s`%s` badge."},
|
||||
{"id": "badges.notify.dm_text", "translation": "@%s granted you the %s`%s` achievement."},
|
||||
{"id": "badges.notify.dm_reason", "translation": "\nWhy? "},
|
||||
{"id": "badges.notify.title", "translation": "%sbadge granted!"},
|
||||
{"id": "badges.notify.channel_text", "translation": "@%s granted @%s the %s`%s` badge."},
|
||||
{"id": "badges.notify.title", "translation": "%sachievement granted!"},
|
||||
{"id": "badges.notify.channel_text", "translation": "@%s granted @%s the %s`%s` achievement."},
|
||||
{"id": "badges.notify.no_permission_channel", "translation": "You don't have permissions to notify the grant on this channel."}
|
||||
]
|
||||
@ -18,6 +18,7 @@ type Bundle i18n.Bundle
|
||||
func Init() *Bundle {
|
||||
bundle := i18n.NewBundle(language.Russian)
|
||||
_, _ = bundle.LoadMessageFileFS(i18nFiles, "en.json")
|
||||
_, _ = bundle.LoadMessageFileFS(i18nFiles, "ru.json")
|
||||
|
||||
return (*Bundle)(bundle)
|
||||
}
|
||||
|
||||
@ -1,21 +1,21 @@
|
||||
[
|
||||
{"id": "badges.dialog.create_badge.title", "translation": "Создать значок"},
|
||||
{"id": "badges.dialog.create_badge.title", "translation": "Создать достижение"},
|
||||
{"id": "badges.dialog.create_badge.submit", "translation": "Создать"},
|
||||
{"id": "badges.dialog.edit_badge.title", "translation": "Редактировать значок"},
|
||||
{"id": "badges.dialog.edit_badge.title", "translation": "Редактировать достижение"},
|
||||
{"id": "badges.dialog.edit_badge.submit", "translation": "Сохранить"},
|
||||
{"id": "badges.dialog.create_type.title", "translation": "Создать тип"},
|
||||
{"id": "badges.dialog.create_type.submit", "translation": "Создать"},
|
||||
{"id": "badges.dialog.edit_type.title", "translation": "Редактировать тип"},
|
||||
{"id": "badges.dialog.edit_type.submit", "translation": "Сохранить"},
|
||||
{"id": "badges.dialog.grant.title", "translation": "Выдать значок"},
|
||||
{"id": "badges.dialog.grant.title", "translation": "Выдать достижение"},
|
||||
{"id": "badges.dialog.grant.submit", "translation": "Выдать"},
|
||||
{"id": "badges.dialog.grant.intro", "translation": "Выдать значок пользователю @%s"},
|
||||
{"id": "badges.dialog.grant.intro", "translation": "Выдать достижение пользователю @%s"},
|
||||
{"id": "badges.dialog.create_subscription.title", "translation": "Создать подписку"},
|
||||
{"id": "badges.dialog.create_subscription.submit", "translation": "Добавить"},
|
||||
{"id": "badges.dialog.create_subscription.intro", "translation": "Выберите тип значка, на который хотите подписать этот канал."},
|
||||
{"id": "badges.dialog.create_subscription.intro", "translation": "Выберите тип достижения, на который хотите подписать этот канал."},
|
||||
{"id": "badges.dialog.delete_subscription.title", "translation": "Удалить подписку"},
|
||||
{"id": "badges.dialog.delete_subscription.submit", "translation": "Удалить"},
|
||||
{"id": "badges.dialog.delete_subscription.intro", "translation": "Выберите тип значка, подписку на который хотите удалить из этого канала."},
|
||||
{"id": "badges.dialog.delete_subscription.intro", "translation": "Выберите тип достижения, подписку на который хотите удалить из этого канала."},
|
||||
|
||||
{"id": "badges.field.name", "translation": "Название"},
|
||||
{"id": "badges.field.description", "translation": "Описание"},
|
||||
@ -23,45 +23,46 @@
|
||||
{"id": "badges.field.image.help", "translation": "Введите название эмодзи"},
|
||||
{"id": "badges.field.type", "translation": "Тип"},
|
||||
{"id": "badges.field.multiple", "translation": "Многократный"},
|
||||
{"id": "badges.field.multiple.help", "translation": "Можно ли выдавать этот значок несколько раз"},
|
||||
{"id": "badges.field.delete_badge", "translation": "Удалить значок"},
|
||||
{"id": "badges.field.delete_badge.help", "translation": "ВНИМАНИЕ: если отметить, значок будет удалён безвозвратно."},
|
||||
{"id": "badges.field.everyone_can_create", "translation": "Все могут создавать значки"},
|
||||
{"id": "badges.field.everyone_can_create.help", "translation": "Любой пользователь может создать значок этого типа"},
|
||||
{"id": "badges.field.multiple.help", "translation": "Можно ли выдавать это достижение несколько раз"},
|
||||
{"id": "badges.field.delete_badge", "translation": "Удалить достижение"},
|
||||
{"id": "badges.field.delete_badge.help", "translation": "ВНИМАНИЕ: если отметить, достижение будет удалён безвозвратно."},
|
||||
{"id": "badges.field.everyone_can_create", "translation": "Все могут создавать достижения"},
|
||||
{"id": "badges.field.everyone_can_create.help", "translation": "Любой пользователь может создать достижение этого типа"},
|
||||
{"id": "badges.field.allowlist_create", "translation": "Список допущенных к созданию"},
|
||||
{"id": "badges.field.allowlist_create.help", "translation": "Укажите имена пользователей через запятую (,), которые могут создавать значки этого типа."},
|
||||
{"id": "badges.field.everyone_can_grant", "translation": "Все могут выдавать значки"},
|
||||
{"id": "badges.field.everyone_can_grant.help", "translation": "Любой пользователь может выдать значок этого типа"},
|
||||
{"id": "badges.field.allowlist_create.help", "translation": "Укажите имена пользователей через запятую (,), которые могут создавать достижения этого типа."},
|
||||
{"id": "badges.field.everyone_can_grant", "translation": "Все могут выдавать достижения"},
|
||||
{"id": "badges.field.everyone_can_grant.help", "translation": "Любой пользователь может выдать достижение этого типа"},
|
||||
{"id": "badges.field.allowlist_grant", "translation": "Список допущенных к выдаче"},
|
||||
{"id": "badges.field.allowlist_grant.help", "translation": "Укажите имена пользователей через запятую (,), которые могут выдавать значки этого типа."},
|
||||
{"id": "badges.field.allowlist_grant.help", "translation": "Укажите имена пользователей через запятую (,), которые могут выдавать достижения этого типа."},
|
||||
{"id": "badges.field.delete_type", "translation": "Удалить тип"},
|
||||
{"id": "badges.field.delete_type.help", "translation": "ВНИМАНИЕ: если отметить, этот тип и все связанные значки будут удалены безвозвратно."},
|
||||
{"id": "badges.field.delete_type.help", "translation": "ВНИМАНИЕ: если отметить, этот тип и все связанные достижения будут удалены безвозвратно."},
|
||||
{"id": "badges.field.user", "translation": "Пользователь"},
|
||||
{"id": "badges.field.badge", "translation": "Значок"},
|
||||
{"id": "badges.field.badge", "translation": "Достижение"},
|
||||
{"id": "badges.field.reason", "translation": "Причина"},
|
||||
{"id": "badges.field.reason.help", "translation": "Причина выдачи значка. Будет видна пользователю и в уведомлениях о выдаче (например, в подписках)."},
|
||||
{"id": "badges.field.reason.help", "translation": "Причина выдачи достижения. Будет видна пользователю и в уведомлениях о выдаче (например, в подписках)."},
|
||||
{"id": "badges.field.notify_here", "translation": "Уведомить в этом канале"},
|
||||
{"id": "badges.field.notify_here.help", "translation": "Если отметить, бот отправит сообщение в этот канал о том, что вы выдали значок этому пользователю."},
|
||||
{"id": "badges.field.notify_here.help", "translation": "Если отметить, бот отправит сообщение в этот канал о том, что вы выдали достижение этому пользователю."},
|
||||
|
||||
{"id": "badges.error.unknown", "translation": "Произошла неизвестная ошибка. Обратитесь к системному администратору."},
|
||||
{"id": "badges.error.cannot_get_user", "translation": "Не удалось получить пользователя."},
|
||||
{"id": "badges.error.only_sysadmin_clean", "translation": "Только системный администратор может очистить базу значков."},
|
||||
{"id": "badges.error.only_sysadmin_clean", "translation": "Только системный администратор может очистить базу достижений."},
|
||||
{"id": "badges.error.specify_create", "translation": "Укажите, что вы хотите создать."},
|
||||
{"id": "badges.error.create_badge_or_type", "translation": "Можно создать badge или type"},
|
||||
{"id": "badges.error.no_types_available", "translation": "Вы не можете создать значки ни одного типа."},
|
||||
{"id": "badges.error.must_set_badge_id", "translation": "Необходимо указать ID значка"},
|
||||
{"id": "badges.error.cannot_edit_badge", "translation": "У вас нет прав на редактирование этого значка"},
|
||||
{"id": "badges.error.no_types_available", "translation": "Вы не можете создать достижения ни одного типа."},
|
||||
{"id": "badges.error.must_set_badge_id", "translation": "Необходимо указать ID достижения"},
|
||||
{"id": "badges.error.cannot_edit_badge", "translation": "У вас нет прав на редактирование этого достижения"},
|
||||
{"id": "badges.error.specify_edit", "translation": "Укажите, что вы хотите отредактировать."},
|
||||
{"id": "badges.error.edit_badge_or_type", "translation": "Можно редактировать badge или type"},
|
||||
{"id": "badges.error.no_permissions_edit_type", "translation": "У вас нет прав на редактирование типа значков."},
|
||||
{"id": "badges.error.no_permissions_edit_type", "translation": "У вас нет прав на редактирование типа достижений."},
|
||||
{"id": "badges.error.must_provide_type_id", "translation": "Необходимо указать ID типа"},
|
||||
{"id": "badges.error.cannot_edit_type", "translation": "У вас нет прав на редактирование этого типа"},
|
||||
{"id": "badges.error.no_permissions_grant", "translation": "У вас нет прав на выдачу этого значка"},
|
||||
{"id": "badges.error.cannot_grant_badge", "translation": "Вы не можете выдать этот значок"},
|
||||
{"id": "badges.error.no_permissions_grant", "translation": "У вас нет прав на выдачу этого достижения"},
|
||||
{"id": "badges.error.cannot_grant_badge", "translation": "Вы не можете выдать это достижение"},
|
||||
{"id": "badges.error.specify_subscription", "translation": "Укажите, что вы хотите сделать."},
|
||||
{"id": "badges.error.create_or_delete_subscription", "translation": "Можно создать или удалить подписку"},
|
||||
{"id": "badges.error.cannot_create_subscription", "translation": "Вы не можете создавать подписки"},
|
||||
{"id": "badges.error.no_permissions_create_type", "translation": "У вас нет прав на создание типа значков."},
|
||||
{"id": "badges.error.no_permissions_create_type", "translation": "У вас нет прав на создание типа достижений."},
|
||||
{"id": "badges.error.already_owned", "translation": "Это достижение уже выдано этому пользователю"},
|
||||
|
||||
{"id": "badges.success.clean", "translation": "Очищено"},
|
||||
{"id": "badges.success.granted", "translation": "Выдано"},
|
||||
@ -72,8 +73,8 @@
|
||||
{"id": "badges.api.empty_emoji", "translation": "Пустой эмодзи"},
|
||||
{"id": "badges.api.invalid_field", "translation": "Некорректное поле"},
|
||||
{"id": "badges.api.type_not_exist", "translation": "Этот тип не существует"},
|
||||
{"id": "badges.api.no_permissions_create_badge", "translation": "У вас нет прав на создание этого значка"},
|
||||
{"id": "badges.api.badge_created", "translation": "Значок `%s` создан."},
|
||||
{"id": "badges.api.no_permissions_create_badge", "translation": "У вас нет прав на создание этого достижения"},
|
||||
{"id": "badges.api.badge_created", "translation": "Достижение `%s` создано."},
|
||||
{"id": "badges.api.no_permissions_create_type", "translation": "У вас нет прав на создание типа"},
|
||||
{"id": "badges.api.cannot_find_user", "translation": "Не удалось найти пользователя"},
|
||||
{"id": "badges.api.error_getting_user", "translation": "Ошибка получения пользователя %s: %v"},
|
||||
@ -83,15 +84,15 @@
|
||||
{"id": "badges.api.could_not_get_type", "translation": "Не удалось получить тип"},
|
||||
{"id": "badges.api.no_permissions_edit_type", "translation": "У вас нет прав на редактирование этого типа"},
|
||||
{"id": "badges.api.type_updated", "translation": "Тип `%s` обновлён."},
|
||||
{"id": "badges.api.cannot_get_badge", "translation": "Не удалось получить значок"},
|
||||
{"id": "badges.api.cannot_edit_badge", "translation": "Вы не можете редактировать этот значок"},
|
||||
{"id": "badges.api.could_not_get_badge", "translation": "Не удалось получить значок"},
|
||||
{"id": "badges.api.no_permissions_edit_badge", "translation": "У вас нет прав на редактирование этого значка"},
|
||||
{"id": "badges.api.badge_updated", "translation": "Значок `%s` обновлён."},
|
||||
{"id": "badges.api.badge_not_found", "translation": "Значок не найден"},
|
||||
{"id": "badges.api.no_permissions_grant", "translation": "У вас нет прав на выдачу этого значка"},
|
||||
{"id": "badges.api.cannot_get_badge", "translation": "Не удалось получить достижение"},
|
||||
{"id": "badges.api.cannot_edit_badge", "translation": "Вы не можете редактировать это достижение"},
|
||||
{"id": "badges.api.could_not_get_badge", "translation": "Не удалось получить достижение"},
|
||||
{"id": "badges.api.no_permissions_edit_badge", "translation": "У вас нет прав на редактирование этого достижения"},
|
||||
{"id": "badges.api.badge_updated", "translation": "Достижение `%s` обновлёно."},
|
||||
{"id": "badges.api.badge_not_found", "translation": "Достижение не найдено"},
|
||||
{"id": "badges.api.no_permissions_grant", "translation": "У вас нет прав на выдачу этого достижения"},
|
||||
{"id": "badges.api.user_not_found", "translation": "Пользователь не найден"},
|
||||
{"id": "badges.api.badge_granted", "translation": "Значок `%s` выдан @%s."},
|
||||
{"id": "badges.api.badge_granted", "translation": "Достижение `%s` выдан @%s."},
|
||||
{"id": "badges.api.cannot_create_subscription", "translation": "Вы не можете создать подписку"},
|
||||
{"id": "badges.api.subscription_added", "translation": "Подписка добавлена"},
|
||||
{"id": "badges.api.cannot_delete_subscription", "translation": "Вы не можете удалить подписку"},
|
||||
@ -99,9 +100,9 @@
|
||||
{"id": "badges.api.cannot_delete_default_type", "translation": "Нельзя удалить тип по умолчанию"},
|
||||
{"id": "badges.api.not_authorized", "translation": "Не авторизован"},
|
||||
|
||||
{"id": "badges.notify.dm_text", "translation": "@%s выдал вам значок %s`%s`."},
|
||||
{"id": "badges.notify.dm_text", "translation": "@%s выдал вам достижение %s`%s`."},
|
||||
{"id": "badges.notify.dm_reason", "translation": "\nПочему? "},
|
||||
{"id": "badges.notify.title", "translation": "%sзначок выдан!"},
|
||||
{"id": "badges.notify.channel_text", "translation": "@%s выдал @%s значок %s`%s`."},
|
||||
{"id": "badges.notify.title", "translation": "%sдостижение выдано!"},
|
||||
{"id": "badges.notify.channel_text", "translation": "@%s выдал @%s достижение %s`%s`."},
|
||||
{"id": "badges.notify.no_permission_channel", "translation": "У вас нет прав на отправку уведомления о выдаче в этот канал."}
|
||||
]
|
||||
|
||||
@ -12,6 +12,7 @@ import (
|
||||
|
||||
var errInvalidBadge = errors.New("invalid badge")
|
||||
var errBadgeNotFound = errors.New("badge not found")
|
||||
var errAlreadyOwned = errors.New("already owned")
|
||||
|
||||
type Store interface {
|
||||
// Interface
|
||||
@ -33,6 +34,8 @@ type Store interface {
|
||||
UpdateBadge(b *badgesmodel.Badge) error
|
||||
DeleteType(tID badgesmodel.BadgeType) error
|
||||
DeleteBadge(bID badgesmodel.BadgeID) error
|
||||
RevokeOwnership(badgeID badgesmodel.BadgeID, userID string, grantTime string) error
|
||||
FindOwnership(badgeID badgesmodel.BadgeID, userID string, grantTime string) (*badgesmodel.Ownership, error)
|
||||
|
||||
AddSubscription(tID badgesmodel.BadgeType, cID string) error
|
||||
RemoveSubscriptions(tID badgesmodel.BadgeType, cID string) error
|
||||
@ -503,6 +506,25 @@ func (s *store) AddSubscription(tID badgesmodel.BadgeType, cID string) error {
|
||||
return s.doAtomic(func() (bool, error) { return s.atomicAddSubscription(toAdd) })
|
||||
}
|
||||
|
||||
func (s *store) FindOwnership(badgeID badgesmodel.BadgeID, userID string, grantTime string) (*badgesmodel.Ownership, error) {
|
||||
ownership, _, err := s.getOwnershipList()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, o := range ownership {
|
||||
if o.Badge == badgeID && o.User == userID && o.Time.Format(time.RFC3339Nano) == grantTime {
|
||||
return &o, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, errors.New("ownership not found")
|
||||
}
|
||||
|
||||
func (s *store) RevokeOwnership(badgeID badgesmodel.BadgeID, userID string, grantTime string) error {
|
||||
return s.doAtomic(func() (bool, error) { return s.atomicRevokeOwnership(badgeID, userID, grantTime) })
|
||||
}
|
||||
|
||||
func (s *store) RemoveSubscriptions(tID badgesmodel.BadgeType, cID string) error {
|
||||
toRemove := badgesmodel.Subscription{ChannelID: cID, TypeID: tID}
|
||||
return s.doAtomic(func() (bool, error) { return s.atomicRemoveSubscription(toRemove) })
|
||||
|
||||
@ -3,6 +3,7 @@ package main
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/larkox/mattermost-plugin-badges/badgesmodel"
|
||||
)
|
||||
@ -107,7 +108,7 @@ func (s *store) atomicAddBadgeToOwnership(o badgesmodel.Ownership, isMultiple bo
|
||||
}
|
||||
|
||||
if !isMultiple && ownership.IsOwned(o.User, o.Badge) {
|
||||
return false, true, nil
|
||||
return false, true, errAlreadyOwned
|
||||
}
|
||||
|
||||
ownership = append(ownership, o)
|
||||
@ -159,6 +160,28 @@ func (s *store) atomicUpdateBadge(b *badgesmodel.Badge) (bool, error) {
|
||||
return s.compareAndSet(KVKeyBadges, data, bb)
|
||||
}
|
||||
|
||||
func (s *store) atomicRevokeOwnership(badgeID badgesmodel.BadgeID, userID string, grantTime string) (bool, error) {
|
||||
ownership, data, err := s.getOwnershipList()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
found := false
|
||||
for i, o := range ownership {
|
||||
if o.Badge == badgeID && o.User == userID && o.Time.Format(time.RFC3339Nano) == grantTime {
|
||||
ownership = append(ownership[:i], ownership[i+1:]...)
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
return s.compareAndSet(KVKeyOwnership, data, ownership)
|
||||
}
|
||||
|
||||
func (s *store) atomicAddSubscription(toAdd badgesmodel.Subscription) (bool, error) {
|
||||
subs, data, err := s.getAllSubscriptions()
|
||||
if err != nil {
|
||||
|
||||
@ -1,16 +1,16 @@
|
||||
{
|
||||
"badges.loading": "Loading...",
|
||||
"badges.no_badges_yet": "No badges yet.",
|
||||
"badges.empty.title": "No badges yet",
|
||||
"badges.empty.description": "Create your first badge to recognize achievements and contributions of your team members.",
|
||||
"badges.badge_not_found": "Badge not found.",
|
||||
"badges.no_badges_yet": "No achievements yet.",
|
||||
"badges.empty.title": "No achievements yet",
|
||||
"badges.empty.description": "Create your first achievement to recognize contributions of your team members.",
|
||||
"badges.badge_not_found": "Achievement not found.",
|
||||
"badges.user_not_found": "User not found.",
|
||||
"badges.unknown": "unknown",
|
||||
|
||||
"badges.rhs.all_badges": "All badges",
|
||||
"badges.rhs.my_badges": "My badges",
|
||||
"badges.rhs.user_badges": "@{username}'s badges",
|
||||
"badges.rhs.badge_details": "Badge Details",
|
||||
"badges.rhs.all_badges": "All achievements",
|
||||
"badges.rhs.my_badges": "My achievements",
|
||||
"badges.rhs.user_badges": "@{username}'s achievements",
|
||||
"badges.rhs.badge_details": "Achievement Details",
|
||||
|
||||
"badges.label.name": "Name:",
|
||||
"badges.label.description": "Description:",
|
||||
@ -27,55 +27,56 @@
|
||||
"badges.granted_to": "Granted to:",
|
||||
"badges.not_granted_yet": "Not granted to anyone yet",
|
||||
|
||||
"badges.set_status": "Set status to this badge",
|
||||
"badges.grant_badge": "Grant badge",
|
||||
"badges.set_status": "Set status to this achievement",
|
||||
"badges.grant_badge": "Grant achievement",
|
||||
"badges.and_more": "and {count} more. Click to see all.",
|
||||
|
||||
"badges.menu.open_list": "Open the list of all badges.",
|
||||
"badges.menu.create_badge": "Create badge",
|
||||
"badges.menu.create_type": "Create badge type",
|
||||
"badges.menu.add_subscription": "Add badge subscription",
|
||||
"badges.menu.remove_subscription": "Remove badge subscription",
|
||||
"badges.menu.open_list": "Open the list of all achievements.",
|
||||
"badges.menu.create_badge": "Create achievement",
|
||||
"badges.menu.create_type": "Create achievement type",
|
||||
"badges.menu.add_subscription": "Add achievement subscription",
|
||||
"badges.menu.remove_subscription": "Remove achievement subscription",
|
||||
|
||||
"badges.sidebar.title": "Badges",
|
||||
"badges.popover.title": "Badges",
|
||||
"badges.sidebar.title": "Achievements",
|
||||
"badges.popover.title": "Achievements",
|
||||
|
||||
"badges.admin.label": "Achievements Administrators:",
|
||||
"badges.admin.placeholder": "Start typing a name...",
|
||||
"badges.admin.help_text": "These users will be considered achievements plugin administrators. They can create types, as well as modify and grant any badges.",
|
||||
"badges.admin.help_text": "These users will be considered achievements plugin administrators. They can create types, as well as modify and grant any achievements.",
|
||||
"badges.admin.no_results": "No users found",
|
||||
|
||||
"badges.rhs.create_badge": "+ Create badge",
|
||||
"badges.rhs.create_badge": "+ Create achievement",
|
||||
"badges.rhs.edit_badge": "Edit",
|
||||
"badges.rhs.types": "Types",
|
||||
"badges.rhs.create_type": "+ Create type",
|
||||
|
||||
"badges.modal.create_badge_title": "Create Badge",
|
||||
"badges.modal.edit_badge_title": "Edit Badge",
|
||||
"badges.modal.create_badge_title": "Create Achievement",
|
||||
"badges.modal.edit_badge_title": "Edit Achievement",
|
||||
"badges.modal.field_name": "Name",
|
||||
"badges.modal.field_name_placeholder": "Badge name (max 20 chars)",
|
||||
"badges.modal.field_name_placeholder": "Achievement name (max 20 chars)",
|
||||
"badges.modal.field_description": "Description",
|
||||
"badges.modal.field_description_placeholder": "Badge description (max 120 chars)",
|
||||
"badges.modal.field_description_placeholder": "Achievement description (max 120 chars)",
|
||||
"badges.modal.field_image": "Emoji",
|
||||
"badges.modal.field_image_placeholder": "Emoji name (e.g. star)",
|
||||
"badges.modal.field_type": "Type",
|
||||
"badges.modal.field_type_placeholder": "Select badge type",
|
||||
"badges.modal.field_type_placeholder": "Select achievement type",
|
||||
"badges.modal.field_multiple": "Can be granted multiple times",
|
||||
"badges.modal.create_new_type": "+ Create new type",
|
||||
"badges.modal.new_type_name": "Type name",
|
||||
"badges.modal.new_type_name_placeholder": "Type name (max 20 chars)",
|
||||
"badges.modal.new_type_everyone_create": "Everyone can create badges",
|
||||
"badges.modal.new_type_everyone_grant": "Everyone can grant badges",
|
||||
"badges.modal.new_type_everyone_create": "Everyone can create achievements",
|
||||
"badges.modal.new_type_everyone_grant": "Everyone can grant achievements",
|
||||
"badges.modal.btn_cancel": "Cancel",
|
||||
"badges.modal.btn_create": "Create",
|
||||
"badges.modal.btn_save": "Save",
|
||||
"badges.modal.btn_creating": "Saving...",
|
||||
"badges.modal.btn_delete": "Delete badge",
|
||||
"badges.modal.btn_delete": "Delete achievement",
|
||||
"badges.modal.btn_confirm_delete": "Yes, delete",
|
||||
"badges.modal.confirm_delete": "Are you sure?",
|
||||
"badges.modal.confirm_delete_badge": "Delete achievement \"{name}\"?",
|
||||
"badges.modal.error_generic": "An error occurred",
|
||||
"badges.modal.error_type_name_required": "Enter type name",
|
||||
"badges.modal.error_type_required": "Select badge type",
|
||||
"badges.modal.error_type_required": "Select achievement type",
|
||||
"badges.modal.create_type_title": "Create Type",
|
||||
"badges.modal.edit_type_title": "Edit Type",
|
||||
"badges.modal.btn_delete_type": "Delete type",
|
||||
@ -83,65 +84,73 @@
|
||||
"badges.modal.confirm_delete_type": "Delete type \"{name}\"?",
|
||||
"badges.modal.btn_confirm_delete_type": "Yes, delete",
|
||||
|
||||
"badges.types.badge_count": "{count, plural, one {# badge} other {# badges}}",
|
||||
"badges.types.badge_count": "{count, plural, one {# achievement} other {# achievements}}",
|
||||
"badges.types.everyone_can_create": "Everyone creates",
|
||||
"badges.types.everyone_can_grant": "Everyone grants",
|
||||
"badges.types.is_default": "Default",
|
||||
"badges.types.confirm_delete": "Delete type \"{name}\" and all its badges?",
|
||||
"badges.types.confirm_delete": "Delete type \"{name}\" and all its achievements?",
|
||||
"badges.types.empty": "No types yet",
|
||||
"badges.types.no_badges": "No badges in this type",
|
||||
"badges.types.no_badges": "No achievements in this type",
|
||||
"badges.rhs.back_to_types": "Back to types",
|
||||
|
||||
"badges.modal.allowlist_create": "Allowlist for creation",
|
||||
"badges.modal.allowlist_create_help": "Users who can create badges of this type.",
|
||||
"badges.modal.allowlist_create_help": "Users who can create achievements of this type.",
|
||||
"badges.modal.allowlist_grant": "Allowlist for granting",
|
||||
"badges.modal.allowlist_grant_help": "Users who can grant badges of this type.",
|
||||
"badges.modal.allowlist_grant_help": "Users who can grant achievements of this type.",
|
||||
"badges.modal.allowlist_placeholder": "user-1, user-2, user-3",
|
||||
|
||||
"badges.grant.title": "Grant Badge",
|
||||
"badges.grant.intro": "Grant badge to @{username}",
|
||||
"badges.grant.field_badge": "Badge",
|
||||
"badges.grant.field_badge_placeholder": "Select a badge",
|
||||
"badges.grant.no_badges": "No badges available",
|
||||
"badges.grant.title": "Grant Achievement",
|
||||
"badges.grant.intro": "Grant achievement to @{username}",
|
||||
"badges.grant.field_badge": "Achievement",
|
||||
"badges.grant.field_badge_placeholder": "Select an achievement",
|
||||
"badges.grant.no_badges": "No achievements available",
|
||||
"badges.grant.field_reason": "Reason",
|
||||
"badges.grant.field_reason_placeholder": "Why is this badge being granted? (optional)",
|
||||
"badges.grant.field_reason_placeholder": "Why is this achievement being granted? (optional)",
|
||||
"badges.grant.notify_here": "Notify in channel",
|
||||
"badges.grant.btn_grant": "Grant",
|
||||
|
||||
"badges.revoke.btn": "Revoke",
|
||||
"badges.revoke.confirm": "Revoke achievement?",
|
||||
"badges.revoke.confirm_yes": "Yes",
|
||||
|
||||
"badges.subscription.title_create": "Add Subscription",
|
||||
"badges.subscription.title_delete": "Remove Subscription",
|
||||
"badges.subscription.field_type": "Badge Type",
|
||||
"badges.subscription.field_type_placeholder": "Select badge type",
|
||||
"badges.subscription.field_type": "Achievement Type",
|
||||
"badges.subscription.field_type_placeholder": "Select achievement type",
|
||||
"badges.subscription.no_types": "No types available",
|
||||
"badges.subscription.btn_create": "Add",
|
||||
"badges.subscription.btn_delete": "Remove",
|
||||
|
||||
"badges.error.invalid_badge_id": "Badge not specified",
|
||||
"badges.error.invalid_badge_id": "Achievement not specified",
|
||||
"badges.error.invalid_user_id": "User not specified",
|
||||
"badges.error.no_permission_grant": "Insufficient permissions to grant this badge",
|
||||
"badges.error.cannot_grant_badge": "Failed to grant badge",
|
||||
"badges.error.no_permission_grant": "Insufficient permissions to grant this achievement",
|
||||
"badges.error.cannot_grant_badge": "Failed to grant achievement",
|
||||
"badges.error.user_not_found": "User not found",
|
||||
"badges.error.invalid_type_id": "Badge type not specified",
|
||||
"badges.error.invalid_type_id": "Achievement type not specified",
|
||||
"badges.error.no_permission_subscription": "Insufficient permissions to manage subscriptions",
|
||||
"badges.error.cannot_create_subscription": "Failed to create subscription",
|
||||
"badges.error.cannot_delete_subscription": "Failed to delete subscription",
|
||||
"badges.error.ownership_not_found": "Ownership not found",
|
||||
"badges.error.no_permission_revoke": "Insufficient permissions to revoke",
|
||||
"badges.error.cannot_revoke": "Failed to revoke",
|
||||
"badges.error.already_owned": "This achievement is already owned by this user",
|
||||
|
||||
"badges.error.unknown": "An error occurred",
|
||||
"badges.error.cannot_get_user": "Failed to get user data",
|
||||
"badges.error.cannot_get_types": "Failed to load types",
|
||||
"badges.error.cannot_get_badges": "Failed to load badges",
|
||||
"badges.error.cannot_get_badges": "Failed to load achievements",
|
||||
"badges.error.invalid_request": "Invalid request format",
|
||||
"badges.error.invalid_name": "Name is required",
|
||||
"badges.error.invalid_image": "Emoji is required",
|
||||
"badges.error.type_not_found": "Badge type not found",
|
||||
"badges.error.badge_not_found": "Badge not found",
|
||||
"badges.error.type_not_found": "Achievement type not found",
|
||||
"badges.error.badge_not_found": "Achievement not found",
|
||||
"badges.error.no_permission": "Insufficient permissions",
|
||||
"badges.error.missing_badge_id": "Badge ID is missing",
|
||||
"badges.error.missing_badge_id": "Achievement ID is missing",
|
||||
"badges.error.missing_type_id": "Type ID is missing",
|
||||
"badges.error.cannot_create_badge": "Failed to create badge",
|
||||
"badges.error.cannot_create_badge": "Failed to create achievement",
|
||||
"badges.error.cannot_create_type": "Failed to create type",
|
||||
"badges.error.cannot_update_badge": "Failed to update badge",
|
||||
"badges.error.cannot_delete_badge": "Failed to delete badge",
|
||||
"badges.error.cannot_update_badge": "Failed to update achievement",
|
||||
"badges.error.cannot_delete_badge": "Failed to delete achievement",
|
||||
"badges.error.cannot_update_type": "Failed to update type",
|
||||
"badges.error.cannot_delete_type": "Failed to delete type",
|
||||
|
||||
|
||||
@ -1,16 +1,16 @@
|
||||
{
|
||||
"badges.loading": "Загрузка...",
|
||||
"badges.no_badges_yet": "Значков пока нет.",
|
||||
"badges.empty.title": "Значков пока нет",
|
||||
"badges.empty.description": "Создайте первый значок, чтобы отмечать достижения и заслуги участников команды.",
|
||||
"badges.badge_not_found": "Значок не найден.",
|
||||
"badges.no_badges_yet": "Достижений пока нет.",
|
||||
"badges.empty.title": "Достижений пока нет",
|
||||
"badges.empty.description": "Создайте первое достижение, чтобы отмечать заслуги участников команды.",
|
||||
"badges.badge_not_found": "Достижение не найдено.",
|
||||
"badges.user_not_found": "Пользователь не найден.",
|
||||
"badges.unknown": "неизвестно",
|
||||
|
||||
"badges.rhs.all_badges": "Все значки",
|
||||
"badges.rhs.my_badges": "Мои значки",
|
||||
"badges.rhs.user_badges": "Значки @{username}",
|
||||
"badges.rhs.badge_details": "Детали значка",
|
||||
"badges.rhs.all_badges": "Все достижения",
|
||||
"badges.rhs.my_badges": "Мои достижения",
|
||||
"badges.rhs.user_badges": "Достижения @{username}",
|
||||
"badges.rhs.badge_details": "Детали достижения",
|
||||
|
||||
"badges.label.name": "Название:",
|
||||
"badges.label.description": "Описание:",
|
||||
@ -28,54 +28,55 @@
|
||||
"badges.not_granted_yet": "Ещё никому не выдан",
|
||||
|
||||
"badges.set_status": "Установить как статус",
|
||||
"badges.grant_badge": "Выдать значок",
|
||||
"badges.grant_badge": "Выдать достижение",
|
||||
"badges.and_more": "и ещё {count}. Нажмите, чтобы увидеть все.",
|
||||
|
||||
"badges.menu.open_list": "Открыть список всех значков.",
|
||||
"badges.menu.create_badge": "Создать значок",
|
||||
"badges.menu.create_type": "Создать тип значков",
|
||||
"badges.menu.add_subscription": "Добавить подписку на значки",
|
||||
"badges.menu.remove_subscription": "Удалить подписку на значки",
|
||||
"badges.menu.open_list": "Открыть список всех достижений.",
|
||||
"badges.menu.create_badge": "Создать достижение",
|
||||
"badges.menu.create_type": "Создать тип достижений",
|
||||
"badges.menu.add_subscription": "Добавить подписку на достижения",
|
||||
"badges.menu.remove_subscription": "Удалить подписку на достижения",
|
||||
|
||||
"badges.sidebar.title": "Значки",
|
||||
"badges.popover.title": "Значки",
|
||||
"badges.sidebar.title": "Достижения",
|
||||
"badges.popover.title": "Достижения",
|
||||
|
||||
"badges.admin.label": "Администраторы достижений:",
|
||||
"badges.admin.placeholder": "Начните вводить имя...",
|
||||
"badges.admin.help_text": "Эти пользователи будут считаться администраторами плагина достижений. Они могут создавать типы, а также изменять и выдавать любые значки.",
|
||||
"badges.admin.help_text": "Эти пользователи будут считаться администраторами плагина достижений. Они могут создавать типы, а также изменять и выдавать любые достижения.",
|
||||
"badges.admin.no_results": "Пользователь не найден",
|
||||
|
||||
"badges.rhs.create_badge": "+ Создать значок",
|
||||
"badges.rhs.create_badge": "+ Создать достижение",
|
||||
"badges.rhs.edit_badge": "Редактировать",
|
||||
"badges.rhs.types": "Типы",
|
||||
"badges.rhs.create_type": "+ Создать тип",
|
||||
|
||||
"badges.modal.create_badge_title": "Создать значок",
|
||||
"badges.modal.edit_badge_title": "Редактировать значок",
|
||||
"badges.modal.create_badge_title": "Создать достижение",
|
||||
"badges.modal.edit_badge_title": "Редактировать достижение",
|
||||
"badges.modal.field_name": "Название",
|
||||
"badges.modal.field_name_placeholder": "Название значка (макс. 20 символов)",
|
||||
"badges.modal.field_name_placeholder": "Название достижения (макс. 20 символов)",
|
||||
"badges.modal.field_description": "Описание",
|
||||
"badges.modal.field_description_placeholder": "Описание значка (макс. 120 символов)",
|
||||
"badges.modal.field_description_placeholder": "Описание достижения (макс. 120 символов)",
|
||||
"badges.modal.field_image": "Эмодзи",
|
||||
"badges.modal.field_image_placeholder": "Название эмодзи (напр. star)",
|
||||
"badges.modal.field_type": "Тип",
|
||||
"badges.modal.field_type_placeholder": "Выберите тип значка",
|
||||
"badges.modal.field_type_placeholder": "Выберите тип достижения",
|
||||
"badges.modal.field_multiple": "Можно выдавать несколько раз",
|
||||
"badges.modal.create_new_type": "+ Создать новый тип",
|
||||
"badges.modal.new_type_name": "Название типа",
|
||||
"badges.modal.new_type_name_placeholder": "Название типа (макс. 20 символов)",
|
||||
"badges.modal.new_type_everyone_create": "Все могут создавать значки",
|
||||
"badges.modal.new_type_everyone_grant": "Все могут выдавать значки",
|
||||
"badges.modal.new_type_everyone_create": "Все могут создавать достижения",
|
||||
"badges.modal.new_type_everyone_grant": "Все могут выдавать достижения",
|
||||
"badges.modal.btn_cancel": "Отмена",
|
||||
"badges.modal.btn_create": "Создать",
|
||||
"badges.modal.btn_save": "Сохранить",
|
||||
"badges.modal.btn_creating": "Сохранение...",
|
||||
"badges.modal.btn_delete": "Удалить значок",
|
||||
"badges.modal.btn_delete": "Удалить достижение",
|
||||
"badges.modal.btn_confirm_delete": "Да, удалить",
|
||||
"badges.modal.confirm_delete": "Вы уверены?",
|
||||
"badges.modal.confirm_delete_badge": "Удалить достижение «{name}»?",
|
||||
"badges.modal.error_generic": "Произошла ошибка",
|
||||
"badges.modal.error_type_name_required": "Введите название типа",
|
||||
"badges.modal.error_type_required": "Выберите тип значка",
|
||||
"badges.modal.error_type_required": "Выберите тип достижения",
|
||||
"badges.modal.create_type_title": "Создать тип",
|
||||
"badges.modal.edit_type_title": "Редактировать тип",
|
||||
"badges.modal.btn_delete_type": "Удалить тип",
|
||||
@ -83,65 +84,73 @@
|
||||
"badges.modal.confirm_delete_type": "Удалить тип «{name}»?",
|
||||
"badges.modal.btn_confirm_delete_type": "Да, удалить",
|
||||
|
||||
"badges.types.badge_count": "{count, plural, one {# значок} few {# значка} many {# значков} other {# значков}}",
|
||||
"badges.types.badge_count": "{count, plural, one {# достижение} few {# достижения} many {# достижений} other {# достижений}}",
|
||||
"badges.types.everyone_can_create": "Все создают",
|
||||
"badges.types.everyone_can_grant": "Все выдают",
|
||||
"badges.types.is_default": "По умолчанию",
|
||||
"badges.types.confirm_delete": "Удалить тип «{name}» и все его значки?",
|
||||
"badges.types.confirm_delete": "Удалить тип «{name}» и все его достижения?",
|
||||
"badges.types.empty": "Типов пока нет",
|
||||
"badges.types.no_badges": "В этом типе нет значков",
|
||||
"badges.types.no_badges": "В этом типе нет достижений",
|
||||
"badges.rhs.back_to_types": "Назад к типам",
|
||||
|
||||
"badges.modal.allowlist_create": "Список допущенных к созданию",
|
||||
"badges.modal.allowlist_create_help": "Пользователи, которые могут создавать значки этого типа.",
|
||||
"badges.modal.allowlist_create_help": "Пользователи, которые могут создавать достижения этого типа.",
|
||||
"badges.modal.allowlist_grant": "Список допущенных к выдаче",
|
||||
"badges.modal.allowlist_grant_help": "Пользователи, которые могут выдавать значки этого типа.",
|
||||
"badges.modal.allowlist_grant_help": "Пользователи, которые могут выдавать достижения этого типа.",
|
||||
"badges.modal.allowlist_placeholder": "user-1, user-2, user-3",
|
||||
|
||||
"badges.grant.title": "Выдать значок",
|
||||
"badges.grant.intro": "Выдать значок пользователю @{username}",
|
||||
"badges.grant.field_badge": "Значок",
|
||||
"badges.grant.field_badge_placeholder": "Выберите значок",
|
||||
"badges.grant.no_badges": "Нет доступных значков",
|
||||
"badges.grant.title": "Выдать достижение",
|
||||
"badges.grant.intro": "Выдать достижение пользователю @{username}",
|
||||
"badges.grant.field_badge": "Достижение",
|
||||
"badges.grant.field_badge_placeholder": "Выберите достижение",
|
||||
"badges.grant.no_badges": "Нет доступных достижений",
|
||||
"badges.grant.field_reason": "Причина",
|
||||
"badges.grant.field_reason_placeholder": "За что выдаётся значок? (необязательно)",
|
||||
"badges.grant.field_reason_placeholder": "За что выдаётся достижение? (необязательно)",
|
||||
"badges.grant.notify_here": "Уведомить в канале",
|
||||
"badges.grant.btn_grant": "Выдать",
|
||||
|
||||
"badges.revoke.btn": "Снять достижение",
|
||||
"badges.revoke.confirm": "Снять достижение?",
|
||||
"badges.revoke.confirm_yes": "Да",
|
||||
|
||||
"badges.subscription.title_create": "Добавить подписку",
|
||||
"badges.subscription.title_delete": "Удалить подписку",
|
||||
"badges.subscription.field_type": "Тип значков",
|
||||
"badges.subscription.field_type_placeholder": "Выберите тип значков",
|
||||
"badges.subscription.field_type": "Тип достижений",
|
||||
"badges.subscription.field_type_placeholder": "Выберите тип достижений",
|
||||
"badges.subscription.no_types": "Нет доступных типов",
|
||||
"badges.subscription.btn_create": "Добавить",
|
||||
"badges.subscription.btn_delete": "Удалить",
|
||||
|
||||
"badges.error.invalid_badge_id": "Не указан значок",
|
||||
"badges.error.invalid_badge_id": "Не указано достижение",
|
||||
"badges.error.invalid_user_id": "Не указан пользователь",
|
||||
"badges.error.no_permission_grant": "Недостаточно прав для выдачи этого значка",
|
||||
"badges.error.cannot_grant_badge": "Не удалось выдать значок",
|
||||
"badges.error.no_permission_grant": "Недостаточно прав для выдачи этого достижения",
|
||||
"badges.error.cannot_grant_badge": "Не удалось выдать достижение",
|
||||
"badges.error.user_not_found": "Пользователь не найден",
|
||||
"badges.error.invalid_type_id": "Не указан тип значков",
|
||||
"badges.error.invalid_type_id": "Не указан тип достижений",
|
||||
"badges.error.no_permission_subscription": "Недостаточно прав для управления подписками",
|
||||
"badges.error.cannot_create_subscription": "Не удалось создать подписку",
|
||||
"badges.error.cannot_delete_subscription": "Не удалось удалить подписку",
|
||||
"badges.error.ownership_not_found": "Выдача не найдена",
|
||||
"badges.error.no_permission_revoke": "Недостаточно прав для снятия этого достижения",
|
||||
"badges.error.cannot_revoke": "Не удалось снять достижение",
|
||||
"badges.error.already_owned": "Это достижение уже выдано этому пользователю",
|
||||
|
||||
"badges.error.unknown": "Произошла ошибка",
|
||||
"badges.error.cannot_get_user": "Не удалось получить данные пользователя",
|
||||
"badges.error.cannot_get_types": "Не удалось загрузить типы",
|
||||
"badges.error.cannot_get_badges": "Не удалось загрузить значки",
|
||||
"badges.error.cannot_get_badges": "Не удалось загрузить достижения",
|
||||
"badges.error.invalid_request": "Неверный формат запроса",
|
||||
"badges.error.invalid_name": "Необходимо указать название",
|
||||
"badges.error.invalid_image": "Необходимо указать эмодзи",
|
||||
"badges.error.type_not_found": "Тип значка не найден",
|
||||
"badges.error.badge_not_found": "Значок не найден",
|
||||
"badges.error.type_not_found": "Тип достижения не найден",
|
||||
"badges.error.badge_not_found": "Достижение не найдено",
|
||||
"badges.error.no_permission": "Недостаточно прав для выполнения действия",
|
||||
"badges.error.missing_badge_id": "Не указан ID значка",
|
||||
"badges.error.missing_badge_id": "Не указан ID достижения",
|
||||
"badges.error.missing_type_id": "Не указан ID типа",
|
||||
"badges.error.cannot_create_badge": "Не удалось создать значок",
|
||||
"badges.error.cannot_create_badge": "Не удалось создать достижение",
|
||||
"badges.error.cannot_create_type": "Не удалось создать тип",
|
||||
"badges.error.cannot_update_badge": "Не удалось обновить значок",
|
||||
"badges.error.cannot_delete_badge": "Не удалось удалить значок",
|
||||
"badges.error.cannot_update_badge": "Не удалось обновить достижение",
|
||||
"badges.error.cannot_delete_badge": "Не удалось удалить достижение",
|
||||
"badges.error.cannot_update_type": "Не удалось обновить тип",
|
||||
"badges.error.cannot_delete_type": "Не удалось удалить тип",
|
||||
|
||||
|
||||
@ -5,7 +5,7 @@ import {Client4} from 'mattermost-redux/client';
|
||||
import {ClientError} from 'mattermost-redux/client/client4';
|
||||
|
||||
import manifest from 'manifest';
|
||||
import {AllBadgesBadge, Badge, BadgeDetails, BadgeID, BadgeTypeDefinition, CreateBadgeRequest, CreateTypeRequest, GetTypesResponse, GrantBadgeRequest, SubscriptionRequest, UpdateBadgeRequest, UpdateTypeRequest, UserBadge} from 'types/badges';
|
||||
import {AllBadgesBadge, Badge, BadgeDetails, BadgeID, BadgeTypeDefinition, CreateBadgeRequest, CreateTypeRequest, GetTypesResponse, GrantBadgeRequest, RevokeOwnershipRequest, SubscriptionRequest, UpdateBadgeRequest, UpdateTypeRequest, UserBadge} from 'types/badges';
|
||||
|
||||
export default class Client {
|
||||
private url: string;
|
||||
@ -86,6 +86,10 @@ export default class Client {
|
||||
await this.doPost(`${this.url}/deleteSubscription`, req);
|
||||
}
|
||||
|
||||
async revokeOwnership(req: RevokeOwnershipRequest): Promise<void> {
|
||||
await this.doPost(`${this.url}/revokeOwnership`, req);
|
||||
}
|
||||
|
||||
async getChannelSubscriptions(channelID: string): Promise<BadgeTypeDefinition[]> {
|
||||
try {
|
||||
const res = await this.doGet(`${this.url}/getChannelSubscriptions/${channelID}`);
|
||||
|
||||
@ -39,7 +39,7 @@ const BadgesAdminSetting: React.FC<Props> = ({id, value, disabled, onChange, set
|
||||
<div className='help-text'>
|
||||
<FormattedMessage
|
||||
id='badges.admin.help_text'
|
||||
defaultMessage='Эти пользователи будут считаться администраторами плагина достижений. Они могут создавать типы, а также изменять и выдавать любые значки.'
|
||||
defaultMessage='Эти пользователи будут считаться администраторами плагина достижений. Они могут создавать типы, а также изменять и выдавать любые достижения.'
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -21,6 +21,7 @@ import InlineTypeForm from './inline_type_form';
|
||||
import TypeSelect from './type_select';
|
||||
|
||||
import './badge_modal.scss';
|
||||
import ConfirmDialog from 'components/confirm_dialog/confirm_dialog';
|
||||
|
||||
const NEW_TYPE_VALUE = '__new__';
|
||||
|
||||
@ -193,7 +194,7 @@ const BadgeModal: React.FC = () => {
|
||||
typeID = String(createdType.id);
|
||||
}
|
||||
if (!typeID) {
|
||||
setError(intl.formatMessage({id: 'badges.modal.error_type_required', defaultMessage: 'Выберите тип значка'}));
|
||||
setError(intl.formatMessage({id: 'badges.modal.error_type_required', defaultMessage: 'Выберите тип достижения'}));
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
@ -252,8 +253,8 @@ const BadgeModal: React.FC = () => {
|
||||
}
|
||||
|
||||
const title = isEditMode
|
||||
? intl.formatMessage({id: 'badges.modal.edit_badge_title', defaultMessage: 'Редактировать значок'})
|
||||
: intl.formatMessage({id: 'badges.modal.create_badge_title', defaultMessage: 'Создать значок'});
|
||||
? intl.formatMessage({id: 'badges.modal.edit_badge_title', defaultMessage: 'Редактировать достижение'})
|
||||
: intl.formatMessage({id: 'badges.modal.create_badge_title', defaultMessage: 'Создать достижение'});
|
||||
const submitLabel = isEditMode
|
||||
? intl.formatMessage({id: 'badges.modal.btn_save', defaultMessage: 'Сохранить'})
|
||||
: intl.formatMessage({id: 'badges.modal.btn_create', defaultMessage: 'Создать'});
|
||||
@ -294,7 +295,7 @@ const BadgeModal: React.FC = () => {
|
||||
value={form.name}
|
||||
onChange={(e) => updateForm({name: e.target.value})}
|
||||
maxLength={20}
|
||||
placeholder={intl.formatMessage({id: 'badges.modal.field_name_placeholder', defaultMessage: 'Название значка (макс. 20 символов)'})}
|
||||
placeholder={intl.formatMessage({id: 'badges.modal.field_name_placeholder', defaultMessage: 'Название достижения (макс. 20 символов)'})}
|
||||
/>
|
||||
</div>
|
||||
<div className='form-group'>
|
||||
@ -308,7 +309,7 @@ const BadgeModal: React.FC = () => {
|
||||
value={form.description}
|
||||
onChange={(e) => updateForm({description: e.target.value})}
|
||||
maxLength={120}
|
||||
placeholder={intl.formatMessage({id: 'badges.modal.field_description_placeholder', defaultMessage: 'Описание значка (макс. 120 символов)'})}
|
||||
placeholder={intl.formatMessage({id: 'badges.modal.field_description_placeholder', defaultMessage: 'Описание достижения (макс. 120 символов)'})}
|
||||
/>
|
||||
</div>
|
||||
<div className='form-group'>
|
||||
@ -398,45 +399,27 @@ const BadgeModal: React.FC = () => {
|
||||
{error && <div className='error-message'>{error}</div>}
|
||||
{isEditMode && (
|
||||
<div className='delete-section'>
|
||||
{confirmDelete ? (
|
||||
<div className='confirm-delete'>
|
||||
<span>
|
||||
<FormattedMessage
|
||||
id='badges.modal.confirm_delete'
|
||||
defaultMessage='Вы уверены?'
|
||||
/>
|
||||
</span>
|
||||
<button
|
||||
className='btn btn--danger'
|
||||
onClick={handleDelete}
|
||||
disabled={loading}
|
||||
>
|
||||
<FormattedMessage
|
||||
id='badges.modal.btn_confirm_delete'
|
||||
defaultMessage='Да, удалить'
|
||||
/>
|
||||
</button>
|
||||
<button
|
||||
className='btn btn--cancel'
|
||||
onClick={() => setConfirmDelete(false)}
|
||||
>
|
||||
<FormattedMessage
|
||||
id='badges.modal.btn_cancel'
|
||||
defaultMessage='Отмена'
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
) : (
|
||||
<button
|
||||
className='btn btn--danger'
|
||||
onClick={handleDelete}
|
||||
disabled={loading}
|
||||
<button
|
||||
className='btn btn--danger'
|
||||
onClick={handleDelete}
|
||||
disabled={loading}
|
||||
>
|
||||
<FormattedMessage
|
||||
id='badges.modal.btn_delete'
|
||||
defaultMessage='Удалить достижение'
|
||||
/>
|
||||
</button>
|
||||
{confirmDelete && (
|
||||
<ConfirmDialog
|
||||
onConfirm={handleDelete}
|
||||
onCancel={() => setConfirmDelete(false)}
|
||||
>
|
||||
<FormattedMessage
|
||||
id='badges.modal.btn_delete'
|
||||
defaultMessage='Удалить значок'
|
||||
id='badges.modal.confirm_delete_badge'
|
||||
defaultMessage='Удалить достижение «{name}»?'
|
||||
values={{name: form.name || editData?.name}}
|
||||
/>
|
||||
</button>
|
||||
</ConfirmDialog>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
@ -41,7 +41,7 @@ const InlineTypeForm: React.FC<Props> = ({form, onChange}) => {
|
||||
<label htmlFor='newTypeEveryoneCanCreate'>
|
||||
<FormattedMessage
|
||||
id='badges.modal.new_type_everyone_create'
|
||||
defaultMessage='Все могут создавать значки'
|
||||
defaultMessage='Все могут создавать достижения'
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
@ -69,7 +69,7 @@ const InlineTypeForm: React.FC<Props> = ({form, onChange}) => {
|
||||
<label htmlFor='newTypeEveryoneCanGrant'>
|
||||
<FormattedMessage
|
||||
id='badges.modal.new_type_everyone_grant'
|
||||
defaultMessage='Все могут выдавать значки'
|
||||
defaultMessage='Все могут выдавать достижения'
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
@ -36,7 +36,7 @@ const TypeSelect: React.FC<Props> = ({
|
||||
const intl = useIntl();
|
||||
|
||||
const selectedTypeName = types.find((t) => String(t.id) === badgeType)?.name ||
|
||||
intl.formatMessage({id: 'badges.modal.field_type_placeholder', defaultMessage: 'Выберите тип значка'});
|
||||
intl.formatMessage({id: 'badges.modal.field_type_placeholder', defaultMessage: 'Выберите тип достижения'});
|
||||
const triggerLabel = showCreateType ? intl.formatMessage({id: 'badges.modal.create_new_type', defaultMessage: '+ Создать новый тип'}) : selectedTypeName;
|
||||
const confirmType = confirmDeleteTypeId ? types.find((t) => String(t.id) === confirmDeleteTypeId) : null;
|
||||
|
||||
|
||||
@ -30,5 +30,31 @@
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
|
||||
.btn--cancel {
|
||||
background: var(--center-channel-bg, #fff);
|
||||
color: var(--center-channel-color, #3d3c40);
|
||||
border: 1px solid rgba(var(--center-channel-color-rgb, 61, 60, 64), 0.16);
|
||||
border-radius: 4px;
|
||||
padding: 8px 16px;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background: rgba(var(--center-channel-color-rgb, 61, 60, 64), 0.08);
|
||||
}
|
||||
}
|
||||
|
||||
.btn--danger {
|
||||
background: var(--error-text, #d24b4e);
|
||||
color: #fff;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
padding: 8px 16px;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background: color-mix(in srgb, var(--error-text, #d24b4e) 85%, #000);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -11,7 +11,10 @@ type Props = {
|
||||
}
|
||||
|
||||
const ConfirmDialog: React.FC<Props> = ({children, onConfirm, onCancel}) => (
|
||||
<div className='ConfirmDialog__overlay'>
|
||||
<div
|
||||
className='ConfirmDialog__overlay'
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<div className='ConfirmDialog'>
|
||||
<p className='ConfirmDialog__text'>
|
||||
{children}
|
||||
|
||||
@ -45,7 +45,7 @@ const GrantModal: React.FC = () => {
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [closing, setClosing] = useState(false);
|
||||
|
||||
// Выбор значка
|
||||
// Выбор достижения
|
||||
const [badgeDropdownOpen, setBadgeDropdownOpen] = useState(false);
|
||||
const badgeDropdownRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
@ -71,7 +71,7 @@ const GrantModal: React.FC = () => {
|
||||
};
|
||||
fetchBadges();
|
||||
|
||||
// Prefill значка, если передан
|
||||
// Prefill достижения, если передан
|
||||
if (modalData?.prefillBadgeId) {
|
||||
setForm((prev) => ({...prev, badgeId: modalData.prefillBadgeId || ''}));
|
||||
}
|
||||
@ -153,7 +153,7 @@ const GrantModal: React.FC = () => {
|
||||
<h4>
|
||||
<FormattedMessage
|
||||
id='badges.grant.title'
|
||||
defaultMessage='Выдать значок'
|
||||
defaultMessage='Выдать достижение'
|
||||
/>
|
||||
</h4>
|
||||
<button
|
||||
@ -168,7 +168,7 @@ const GrantModal: React.FC = () => {
|
||||
<p className='grant-intro'>
|
||||
<FormattedMessage
|
||||
id='badges.grant.intro'
|
||||
defaultMessage='Выдать значок пользователю @{username}'
|
||||
defaultMessage='Выдать достижение пользователю @{username}'
|
||||
values={{username: modalData?.prefillUser || ''}}
|
||||
/>
|
||||
</p>
|
||||
@ -177,7 +177,7 @@ const GrantModal: React.FC = () => {
|
||||
<label>
|
||||
<FormattedMessage
|
||||
id='badges.grant.field_badge'
|
||||
defaultMessage='Значок'
|
||||
defaultMessage='Достижение'
|
||||
/>
|
||||
<span className='required'>{'*'}</span>
|
||||
</label>
|
||||
@ -199,7 +199,7 @@ const GrantModal: React.FC = () => {
|
||||
/>
|
||||
{' '}{selectedBadge.name}
|
||||
</>
|
||||
) : intl.formatMessage({id: 'badges.grant.field_badge_placeholder', defaultMessage: 'Выберите значок'})}
|
||||
) : intl.formatMessage({id: 'badges.grant.field_badge_placeholder', defaultMessage: 'Выберите достижение'})}
|
||||
</span>
|
||||
<span className='type-select__arrow'>{'▾'}</span>
|
||||
</button>
|
||||
@ -209,7 +209,7 @@ const GrantModal: React.FC = () => {
|
||||
<div className='type-select__option'>
|
||||
<FormattedMessage
|
||||
id='badges.grant.no_badges'
|
||||
defaultMessage='Нет доступных значков'
|
||||
defaultMessage='Нет доступных достижений'
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
@ -244,7 +244,7 @@ const GrantModal: React.FC = () => {
|
||||
value={form.reason}
|
||||
onChange={(e) => updateForm({reason: e.target.value})}
|
||||
maxLength={200}
|
||||
placeholder={intl.formatMessage({id: 'badges.grant.field_reason_placeholder', defaultMessage: 'За что выдаётся значок? (необязательно)'})}
|
||||
placeholder={intl.formatMessage({id: 'badges.grant.field_reason_placeholder', defaultMessage: 'За что выдаётся достижение? (необязательно)'})}
|
||||
/>
|
||||
</div>
|
||||
<div className='checkbox-group'>
|
||||
|
||||
@ -112,13 +112,13 @@ const AllBadges: React.FC<Props> = ({filterTypeId, filterTypeName, actions}) =>
|
||||
<div className='AllBadges__emptyTitle'>
|
||||
<FormattedMessage
|
||||
id='badges.empty.title'
|
||||
defaultMessage='Значков пока нет'
|
||||
defaultMessage='Достижений пока нет'
|
||||
/>
|
||||
</div>
|
||||
<div className='AllBadges__emptyDescription'>
|
||||
<FormattedMessage
|
||||
id='badges.empty.description'
|
||||
defaultMessage='Создайте первый значок, чтобы отмечать достижения и заслуги участников команды.'
|
||||
defaultMessage='Создайте первое достижение, чтобы отмечать заслуги участников команды.'
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@ -127,7 +127,7 @@ const AllBadges: React.FC<Props> = ({filterTypeId, filterTypeName, actions}) =>
|
||||
<div className='AllBadges__empty'>
|
||||
<FormattedMessage
|
||||
id='badges.types.no_badges'
|
||||
defaultMessage='В этом типе нет значков'
|
||||
defaultMessage='В этом типе нет достижений'
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@ -3,6 +3,7 @@ import React, {useState} from 'react';
|
||||
import {FormattedMessage} from 'react-intl';
|
||||
|
||||
import {BadgeTypeDefinition} from '../../types/badges';
|
||||
import ConfirmDialog from '../confirm_dialog/confirm_dialog';
|
||||
|
||||
import './all_types_row.scss';
|
||||
|
||||
@ -44,7 +45,7 @@ const AllTypesRow: React.FC<Props> = ({badgeType, onEdit, onDelete, onClick}: Pr
|
||||
<div className='AllTypesRow__meta'>
|
||||
<FormattedMessage
|
||||
id='badges.types.badge_count'
|
||||
defaultMessage='{count, plural, one {# значок} few {# значка} many {# значков} other {# значков}}'
|
||||
defaultMessage='{count, plural, one {# достижение} few {# достижения} many {# достижений} other {# достижений}}'
|
||||
values={{count: badgeType.badge_count}}
|
||||
/>
|
||||
{badgeType.can_create?.everyone && (
|
||||
@ -71,58 +72,37 @@ const AllTypesRow: React.FC<Props> = ({badgeType, onEdit, onDelete, onClick}: Pr
|
||||
className='AllTypesRow__actions'
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
{!confirmDelete && (
|
||||
<button
|
||||
className='AllTypesRow__btn AllTypesRow__btn--edit'
|
||||
onClick={() => onEdit(badgeType)}
|
||||
>
|
||||
<FormattedMessage
|
||||
id='badges.rhs.edit_badge'
|
||||
defaultMessage='Редактировать'
|
||||
/>
|
||||
</button>
|
||||
{!badgeType.is_default && (
|
||||
<button
|
||||
className='AllTypesRow__btn AllTypesRow__btn--edit'
|
||||
onClick={() => onEdit(badgeType)}
|
||||
className='AllTypesRow__btn AllTypesRow__btn--danger'
|
||||
onClick={handleDelete}
|
||||
>
|
||||
<FormattedMessage
|
||||
id='badges.rhs.edit_badge'
|
||||
defaultMessage='Редактировать'
|
||||
id='badges.modal.delete_type'
|
||||
defaultMessage='Удалить'
|
||||
/>
|
||||
</button>
|
||||
)}
|
||||
{!badgeType.is_default && (
|
||||
<>
|
||||
{confirmDelete ? (
|
||||
<div className='AllTypesRow__confirmDelete'>
|
||||
<span className='AllTypesRow__confirmText'>
|
||||
<FormattedMessage
|
||||
id='badges.modal.confirm_delete'
|
||||
defaultMessage='Вы уверены?'
|
||||
/>
|
||||
</span>
|
||||
<button
|
||||
className='AllTypesRow__btn AllTypesRow__btn--danger'
|
||||
onClick={handleDelete}
|
||||
>
|
||||
<FormattedMessage
|
||||
id='badges.modal.btn_confirm_delete_type'
|
||||
defaultMessage='Да, удалить'
|
||||
/>
|
||||
</button>
|
||||
<button
|
||||
className='AllTypesRow__btn AllTypesRow__btn--cancel'
|
||||
onClick={() => setConfirmDelete(false)}
|
||||
>
|
||||
<FormattedMessage
|
||||
id='badges.modal.btn_cancel'
|
||||
defaultMessage='Отмена'
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
) : (
|
||||
<button
|
||||
className='AllTypesRow__btn AllTypesRow__btn--danger'
|
||||
onClick={handleDelete}
|
||||
>
|
||||
<FormattedMessage
|
||||
id='badges.modal.delete_type'
|
||||
defaultMessage='Удалить'
|
||||
/>
|
||||
</button>
|
||||
)}
|
||||
</>
|
||||
{confirmDelete && (
|
||||
<ConfirmDialog
|
||||
onConfirm={() => onDelete(badgeType)}
|
||||
onCancel={() => setConfirmDelete(false)}
|
||||
>
|
||||
<FormattedMessage
|
||||
id='badges.modal.confirm_delete_type'
|
||||
defaultMessage='Удалить тип «{name}»?'
|
||||
values={{name: badgeType.name}}
|
||||
/>
|
||||
</ConfirmDialog>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -93,7 +93,7 @@ class BadgeDetailsComponent extends React.PureComponent<Props, State> {
|
||||
return (<div>
|
||||
<FormattedMessage
|
||||
id='badges.badge_not_found'
|
||||
defaultMessage='Значок не найден.'
|
||||
defaultMessage='Достижение не найдено.'
|
||||
/>
|
||||
</div>);
|
||||
}
|
||||
@ -108,7 +108,7 @@ class BadgeDetailsComponent extends React.PureComponent<Props, State> {
|
||||
return (<div>
|
||||
<FormattedMessage
|
||||
id='badges.badge_not_found'
|
||||
defaultMessage='Значок не найден.'
|
||||
defaultMessage='Достижение не найдено.'
|
||||
/>
|
||||
</div>);
|
||||
}
|
||||
|
||||
@ -76,7 +76,7 @@ const RHS: React.FC = () => {
|
||||
>
|
||||
<FormattedMessage
|
||||
id='badges.rhs.all_badges'
|
||||
defaultMessage='Все значки'
|
||||
defaultMessage='Все достижения'
|
||||
/>
|
||||
</button>
|
||||
{canEditType && (
|
||||
@ -98,7 +98,7 @@ const RHS: React.FC = () => {
|
||||
>
|
||||
<FormattedMessage
|
||||
id='badges.rhs.create_badge'
|
||||
defaultMessage='+ Создать значок'
|
||||
defaultMessage='+ Создать достижение'
|
||||
/>
|
||||
</button>
|
||||
)}
|
||||
@ -163,6 +163,7 @@ const RHS: React.FC = () => {
|
||||
<UserBadges
|
||||
user={currentUser}
|
||||
isCurrentUser={false}
|
||||
currentUserID={myUser.id}
|
||||
actions={{
|
||||
setRHSView: (view: RHSState) => dispatch(setRHSView(view)),
|
||||
setRHSBadge: (badge: BadgeID | null) => dispatch(setRHSBadge(badge)),
|
||||
@ -176,6 +177,7 @@ const RHS: React.FC = () => {
|
||||
<UserBadges
|
||||
user={myUser}
|
||||
isCurrentUser={true}
|
||||
currentUserID={myUser.id}
|
||||
actions={{
|
||||
setRHSView: (view: RHSState) => dispatch(setRHSView(view)),
|
||||
setRHSBadge: (badge: BadgeID | null) => dispatch(setRHSBadge(badge)),
|
||||
|
||||
@ -73,4 +73,38 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.user-badge-revoke {
|
||||
margin-top: 4px;
|
||||
|
||||
a {
|
||||
font-size: 12px;
|
||||
color: var(--error-text, #d24b4e);
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
||||
&--confirm {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
&__text {
|
||||
font-size: 12px;
|
||||
color: var(--error-text, #d24b4e);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
&__yes {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
&__no {
|
||||
color: rgba(var(--center-channel-color-rgb), 0.56) !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import React from 'react';
|
||||
import React, {useState} from 'react';
|
||||
|
||||
import {FormattedMessage, useIntl} from 'react-intl';
|
||||
|
||||
@ -7,18 +7,42 @@ import Client4 from 'mattermost-redux/client/client4';
|
||||
import {UserBadge} from '../../types/badges';
|
||||
import BadgeImage from '../utils/badge_image';
|
||||
import {markdown} from 'utils/markdown';
|
||||
import Client from '../../client/api';
|
||||
import ConfirmDialog from '../confirm_dialog/confirm_dialog';
|
||||
|
||||
import './user_badge_row.scss';
|
||||
|
||||
type Props = {
|
||||
badge: UserBadge;
|
||||
isCurrentUser: boolean;
|
||||
currentUserID: string;
|
||||
onClick: (badge: UserBadge) => void;
|
||||
onRevoke?: (badge: UserBadge) => void;
|
||||
}
|
||||
|
||||
const UserBadgeRow: React.FC<Props> = ({badge, onClick, isCurrentUser}: Props) => {
|
||||
const UserBadgeRow: React.FC<Props> = ({badge, onClick, isCurrentUser, currentUserID, onRevoke}: Props) => {
|
||||
const intl = useIntl();
|
||||
const time = new Date(badge.time);
|
||||
const [confirmingRevoke, setConfirmingRevoke] = useState(false);
|
||||
|
||||
const canRevoke = badge.granted_by === currentUserID;
|
||||
|
||||
const handleRevoke = async () => {
|
||||
try {
|
||||
const client = new Client();
|
||||
await client.revokeOwnership({
|
||||
badge_id: String(badge.id),
|
||||
user_id: badge.user,
|
||||
time: String(badge.time),
|
||||
});
|
||||
onRevoke?.(badge);
|
||||
} catch {
|
||||
// ignore
|
||||
} finally {
|
||||
setConfirmingRevoke(false);
|
||||
}
|
||||
};
|
||||
|
||||
let reason = null;
|
||||
if (badge.reason) {
|
||||
reason = (
|
||||
@ -50,6 +74,42 @@ const UserBadgeRow: React.FC<Props> = ({badge, onClick, isCurrentUser}: Props) =
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
let revokeAction = null;
|
||||
if (canRevoke && onRevoke) {
|
||||
revokeAction = (
|
||||
<div className='user-badge-revoke'>
|
||||
<a
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
setConfirmingRevoke(true);
|
||||
}}
|
||||
>
|
||||
<FormattedMessage
|
||||
id='badges.revoke.btn'
|
||||
defaultMessage='Снять достижение'
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
);
|
||||
if (confirmingRevoke) {
|
||||
revokeAction = (
|
||||
<>
|
||||
{revokeAction}
|
||||
<ConfirmDialog
|
||||
onConfirm={handleRevoke}
|
||||
onCancel={() => setConfirmingRevoke(false)}
|
||||
>
|
||||
<FormattedMessage
|
||||
id='badges.revoke.confirm'
|
||||
defaultMessage='Снять достижение?'
|
||||
/>
|
||||
</ConfirmDialog>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className='UserBadgesRow'
|
||||
@ -105,6 +165,7 @@ const UserBadgeRow: React.FC<Props> = ({badge, onClick, isCurrentUser}: Props) =
|
||||
</div>
|
||||
{reason}
|
||||
{setStatus}
|
||||
{revokeAction}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -18,6 +18,7 @@ import './user_badges.scss';
|
||||
|
||||
type Props = {
|
||||
isCurrentUser: boolean;
|
||||
currentUserID: string;
|
||||
user: UserProfile | null;
|
||||
actions: {
|
||||
setRHSView: (view: RHSState) => void;
|
||||
@ -84,6 +85,17 @@ class UserBadges extends React.PureComponent<Props, State> {
|
||||
this.props.actions.setRHSView(RHS_STATE_DETAIL);
|
||||
}
|
||||
|
||||
onRevoke = () => {
|
||||
if (!this.props.user) {
|
||||
return;
|
||||
}
|
||||
const c = new Client();
|
||||
this.setState({loading: true});
|
||||
c.getUserBadges(this.props.user.id).then((badges) => {
|
||||
this.setState({badges, loading: false});
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
if (!this.props.user) {
|
||||
return (<div>
|
||||
@ -104,7 +116,7 @@ class UserBadges extends React.PureComponent<Props, State> {
|
||||
return (<div>
|
||||
<FormattedMessage
|
||||
id='badges.no_badges_yet'
|
||||
defaultMessage='Значков пока нет.'
|
||||
defaultMessage='Достижений пока нет.'
|
||||
/>
|
||||
</div>);
|
||||
}
|
||||
@ -113,9 +125,11 @@ class UserBadges extends React.PureComponent<Props, State> {
|
||||
return (
|
||||
<UserBadgeRow
|
||||
isCurrentUser={this.props.isCurrentUser}
|
||||
currentUserID={this.props.currentUserID}
|
||||
key={badge.time}
|
||||
badge={badge}
|
||||
onClick={this.onBadgeClick}
|
||||
onRevoke={this.onRevoke}
|
||||
/>
|
||||
);
|
||||
});
|
||||
@ -123,12 +137,12 @@ class UserBadges extends React.PureComponent<Props, State> {
|
||||
const title = this.props.isCurrentUser ? (
|
||||
<FormattedMessage
|
||||
id='badges.rhs.my_badges'
|
||||
defaultMessage='Мои значки'
|
||||
defaultMessage='Мои достижения'
|
||||
/>
|
||||
) : (
|
||||
<FormattedMessage
|
||||
id='badges.rhs.user_badges'
|
||||
defaultMessage='Значки @{username}'
|
||||
defaultMessage='Достижения @{username}'
|
||||
values={{username: this.props.user.username}}
|
||||
/>
|
||||
);
|
||||
|
||||
@ -128,7 +128,7 @@ const SubscriptionModal: React.FC = () => {
|
||||
<label>
|
||||
<FormattedMessage
|
||||
id='badges.subscription.field_type'
|
||||
defaultMessage='Тип значков'
|
||||
defaultMessage='Тип достижений'
|
||||
/>
|
||||
<span className='required'>{'*'}</span>
|
||||
</label>
|
||||
@ -144,7 +144,7 @@ const SubscriptionModal: React.FC = () => {
|
||||
<span className='type-select__value'>
|
||||
{selectedType
|
||||
? selectedType.name
|
||||
: intl.formatMessage({id: 'badges.subscription.field_type_placeholder', defaultMessage: 'Выберите тип значков'})
|
||||
: intl.formatMessage({id: 'badges.subscription.field_type_placeholder', defaultMessage: 'Выберите тип достижений'})
|
||||
}
|
||||
</span>
|
||||
<span className='type-select__arrow'>{'▾'}</span>
|
||||
|
||||
@ -10,6 +10,7 @@ import Client from 'client/api';
|
||||
import {getServerErrorId} from 'utils/helpers';
|
||||
import CloseIcon from 'components/icons/close_icon';
|
||||
import UserMultiSelect from 'components/user_multi_select';
|
||||
import ConfirmDialog from 'components/confirm_dialog/confirm_dialog';
|
||||
|
||||
const emptyTypeForm: TypeFormData = {
|
||||
name: '',
|
||||
@ -173,7 +174,7 @@ const TypeModal: React.FC = () => {
|
||||
<label htmlFor='typeEveryoneCanCreate'>
|
||||
<FormattedMessage
|
||||
id='badges.modal.new_type_everyone_create'
|
||||
defaultMessage='Все могут создавать значки'
|
||||
defaultMessage='Все могут создавать достижения'
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
@ -192,7 +193,7 @@ const TypeModal: React.FC = () => {
|
||||
<span className='form-group__help'>
|
||||
<FormattedMessage
|
||||
id='badges.modal.allowlist_create_help'
|
||||
defaultMessage='Пользователи, которые могут создавать значки этого типа.'
|
||||
defaultMessage='Пользователи, которые могут создавать достижения этого типа.'
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
@ -207,7 +208,7 @@ const TypeModal: React.FC = () => {
|
||||
<label htmlFor='typeEveryoneCanGrant'>
|
||||
<FormattedMessage
|
||||
id='badges.modal.new_type_everyone_grant'
|
||||
defaultMessage='Все могут выдавать значки'
|
||||
defaultMessage='Все могут выдавать достижения'
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
@ -226,7 +227,7 @@ const TypeModal: React.FC = () => {
|
||||
<span className='form-group__help'>
|
||||
<FormattedMessage
|
||||
id='badges.modal.allowlist_grant_help'
|
||||
defaultMessage='Пользователи, которые могут выдавать значки этого типа.'
|
||||
defaultMessage='Пользователи, которые могут выдавать достижения этого типа.'
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
@ -234,46 +235,27 @@ const TypeModal: React.FC = () => {
|
||||
{error && <div className='error-message'>{error}</div>}
|
||||
{isEditMode && !editData?.is_default && (
|
||||
<div className='delete-section'>
|
||||
{confirmDelete ? (
|
||||
<div className='confirm-delete'>
|
||||
<span>
|
||||
<FormattedMessage
|
||||
id='badges.types.confirm_delete'
|
||||
defaultMessage='Удалить тип «{name}» и все его значки?'
|
||||
values={{name: editData?.name}}
|
||||
/>
|
||||
</span>
|
||||
<button
|
||||
className='btn btn--danger'
|
||||
onClick={handleDelete}
|
||||
disabled={loading}
|
||||
>
|
||||
<FormattedMessage
|
||||
id='badges.modal.btn_confirm_delete'
|
||||
defaultMessage='Да, удалить'
|
||||
/>
|
||||
</button>
|
||||
<button
|
||||
className='btn btn--cancel'
|
||||
onClick={() => setConfirmDelete(false)}
|
||||
>
|
||||
<FormattedMessage
|
||||
id='badges.modal.btn_cancel'
|
||||
defaultMessage='Отмена'
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
) : (
|
||||
<button
|
||||
className='btn btn--danger'
|
||||
onClick={handleDelete}
|
||||
disabled={loading}
|
||||
<button
|
||||
className='btn btn--danger'
|
||||
onClick={handleDelete}
|
||||
disabled={loading}
|
||||
>
|
||||
<FormattedMessage
|
||||
id='badges.modal.btn_delete_type'
|
||||
defaultMessage='Удалить тип'
|
||||
/>
|
||||
</button>
|
||||
{confirmDelete && (
|
||||
<ConfirmDialog
|
||||
onConfirm={handleDelete}
|
||||
onCancel={() => setConfirmDelete(false)}
|
||||
>
|
||||
<FormattedMessage
|
||||
id='badges.modal.btn_delete_type'
|
||||
defaultMessage='Удалить тип'
|
||||
id='badges.types.confirm_delete'
|
||||
defaultMessage='Удалить тип «{name}» и все его достижения?'
|
||||
values={{name: editData?.name}}
|
||||
/>
|
||||
</button>
|
||||
</ConfirmDialog>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
@ -42,7 +42,7 @@ type State = {
|
||||
loaded?: boolean;
|
||||
}
|
||||
|
||||
const MAX_BADGES = 7;
|
||||
const MAX_BADGES = 6;
|
||||
const BADGE_SIZE = 24;
|
||||
|
||||
class BadgeList extends React.PureComponent<Props, State> {
|
||||
@ -229,7 +229,7 @@ class BadgeList extends React.PureComponent<Props, State> {
|
||||
<div><b>
|
||||
<FormattedMessage
|
||||
id='badges.popover.title'
|
||||
defaultMessage='Значки'
|
||||
defaultMessage='Достижения'
|
||||
/>
|
||||
</b></div>
|
||||
<div id='contentContainer' >
|
||||
@ -244,7 +244,7 @@ class BadgeList extends React.PureComponent<Props, State> {
|
||||
<span className={'fa fa-plus-circle'}/>
|
||||
<FormattedMessage
|
||||
id='badges.grant_badge'
|
||||
defaultMessage='Выдать значок'
|
||||
defaultMessage='Выдать достижение'
|
||||
/>
|
||||
</button>
|
||||
<hr className='divider divider--expanded'/>
|
||||
|
||||
@ -128,3 +128,9 @@ export type SubscriptionRequest = {
|
||||
type_id: string;
|
||||
channel_id: string;
|
||||
}
|
||||
|
||||
export type RevokeOwnershipRequest = {
|
||||
badge_id: string;
|
||||
user_id: string;
|
||||
time: string;
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user