diff --git a/mattermost-plugin/plugin.json b/mattermost-plugin/plugin.json index 7c458cc74..a02f513bc 100644 --- a/mattermost-plugin/plugin.json +++ b/mattermost-plugin/plugin.json @@ -6,7 +6,7 @@ "support_url": "https://github.com/mattermost/focalboard/issues", "release_notes_url": "https://github.com/mattermost/focalboard/releases", "icon_path": "assets/starter-template-icon.svg", - "version": "7.9.0", + "version": "7.10.0", "min_server_version": "7.2.0", "server": { "executables": { diff --git a/mattermost-plugin/server/manifest.go b/mattermost-plugin/server/manifest.go index 941134c75..66bea8178 100644 --- a/mattermost-plugin/server/manifest.go +++ b/mattermost-plugin/server/manifest.go @@ -20,7 +20,7 @@ const manifestStr = ` "support_url": "https://github.com/mattermost/focalboard/issues", "release_notes_url": "https://github.com/mattermost/focalboard/releases", "icon_path": "assets/starter-template-icon.svg", - "version": "7.9.0", + "version": "7.10.0", "min_server_version": "7.2.0", "server": { "executables": { diff --git a/mattermost-plugin/webapp/src/components/rhsChannelBoardItem.tsx b/mattermost-plugin/webapp/src/components/rhsChannelBoardItem.tsx index 5f60e71a8..1f03dc003 100644 --- a/mattermost-plugin/webapp/src/components/rhsChannelBoardItem.tsx +++ b/mattermost-plugin/webapp/src/components/rhsChannelBoardItem.tsx @@ -17,8 +17,10 @@ import CompassIcon from '../../../../webapp/src/widgets/icons/compassIcon' import {Permission} from '../../../../webapp/src/constants' -import './rhsChannelBoardItem.scss' import BoardPermissionGate from '../../../../webapp/src/components/permissions/boardPermissionGate' +import TelemetryClient, {TelemetryActions, TelemetryCategory} from '../../../../webapp/src/telemetry/telemetryClient' + +import './rhsChannelBoardItem.scss' const windowAny = (window as SuiteWindow) @@ -36,6 +38,10 @@ const RHSChannelBoardItem = (props: Props) => { } const handleBoardClicked = (boardID: string) => { + // send the telemetry information for the clicked board + const extraData = {teamID: team.id, board: boardID} + TelemetryClient.trackEvent(TelemetryCategory, TelemetryActions.ClickChannelsRHSBoard, extraData) + window.open(`${windowAny.frontendBaseURL}/team/${team.id}/${boardID}`, '_blank', 'noopener') } diff --git a/server/api/api.go b/server/api/api.go index ec376a9c0..be0287257 100644 --- a/server/api/api.go +++ b/server/api/api.go @@ -227,7 +227,7 @@ func jsonStringResponse(w http.ResponseWriter, code int, message string) { //nol fmt.Fprint(w, message) } -func jsonBytesResponse(w http.ResponseWriter, code int, json []byte) { +func jsonBytesResponse(w http.ResponseWriter, code int, json []byte) { //nolint:unparam setResponseHeader(w, "Content-Type", "application/json") w.WriteHeader(code) _, _ = w.Write(json) diff --git a/server/api/files.go b/server/api/files.go index d9f6aa109..bb0ddcfc7 100644 --- a/server/api/files.go +++ b/server/api/files.go @@ -123,37 +123,12 @@ func (a *API) handleServeFile(w http.ResponseWriter, r *http.Request) { auditRec.AddMeta("teamID", board.TeamID) auditRec.AddMeta("filename", filename) - fileInfo, err := a.app.GetFileInfo(filename) + fileInfo, fileReader, err := a.app.GetFile(board.TeamID, boardID, filename) if err != nil && !model.IsErrNotFound(err) { a.errorResponse(w, r, err) return } - if fileInfo != nil && fileInfo.Archived { - fileMetadata := map[string]interface{}{ - "archived": true, - "name": fileInfo.Name, - "size": fileInfo.Size, - "extension": fileInfo.Extension, - } - - data, jsonErr := json.Marshal(fileMetadata) - if jsonErr != nil { - a.logger.Error("failed to marshal archived file metadata", mlog.String("filename", filename), mlog.Err(jsonErr)) - a.errorResponse(w, r, jsonErr) - return - } - - jsonBytesResponse(w, http.StatusBadRequest, data) - return - } - - fileReader, err := a.app.GetFileReader(board.TeamID, boardID, filename) - if err != nil && !errors.Is(err, app.ErrFileNotFound) { - a.errorResponse(w, r, err) - return - } - if errors.Is(err, app.ErrFileNotFound) && board.ChannelID != "" { // prior to moving from workspaces to teams, the filepath was constructed from // workspaceID, which is the channel ID in plugin mode. diff --git a/server/app/files.go b/server/app/files.go index 081b78e14..1474a291a 100644 --- a/server/app/files.go +++ b/server/app/files.go @@ -7,6 +7,7 @@ import ( "path/filepath" "strings" + "github.com/mattermost/focalboard/server/model" mmModel "github.com/mattermost/mattermost-server/v6/model" "github.com/mattermost/focalboard/server/utils" @@ -28,7 +29,7 @@ func (a *App) SaveFile(reader io.Reader, teamID, rootID, filename string) (strin createdFilename := utils.NewID(utils.IDTypeNone) fullFilename := fmt.Sprintf(`%s%s`, createdFilename, fileExtension) - filePath := filepath.Join(teamID, rootID, fullFilename) + filePath := filepath.Join(utils.GetBaseFilePath(), fullFilename) fileSize, appErr := a.filesBackend.WriteFile(reader, filePath) if appErr != nil { @@ -45,7 +46,7 @@ func (a *App) SaveFile(reader io.Reader, teamID, rootID, filename string) (strin CreateAt: now, UpdateAt: now, DeleteAt: 0, - Path: emptyString, + Path: filePath, ThumbnailPath: emptyString, PreviewPath: emptyString, Name: filename, @@ -59,6 +60,7 @@ func (a *App) SaveFile(reader io.Reader, teamID, rootID, filename string) (strin Content: "", RemoteId: nil, } + err := a.store.SaveFileInfo(fileInfo) if err != nil { return "", err @@ -77,6 +79,7 @@ func (a *App) GetFileInfo(filename string) (*mmModel.FileInfo, error) { // will be the fileinfo id. parts := strings.Split(filename, ".") fileInfoID := parts[0][1:] + fileInfo, err := a.store.GetFileInfo(fileInfoID) if err != nil { return nil, err @@ -85,6 +88,40 @@ func (a *App) GetFileInfo(filename string) (*mmModel.FileInfo, error) { return fileInfo, nil } +func (a *App) GetFile(teamID, rootID, fileName string) (*mmModel.FileInfo, filestore.ReadCloseSeeker, error) { + fileInfo, err := a.GetFileInfo(fileName) + if err != nil && !model.IsErrNotFound(err) { + a.logger.Error("111") + return nil, nil, err + } + + var filePath string + + if fileInfo != nil && fileInfo.Path != "" { + filePath = fileInfo.Path + } else { + filePath = filepath.Join(teamID, rootID, fileName) + } + + exists, err := a.filesBackend.FileExists(filePath) + if err != nil { + a.logger.Error(fmt.Sprintf("GetFile: Failed to check if file exists as path. Path: %s, error: %e", filePath, err)) + return nil, nil, err + } + + if !exists { + return nil, nil, ErrFileNotFound + } + + reader, err := a.filesBackend.Reader(filePath) + if err != nil { + a.logger.Error(fmt.Sprintf("GetFile: Failed to get file reader of existing file at path: %s, error: %e", filePath, err)) + return nil, nil, err + } + + return fileInfo, reader, nil +} + func (a *App) GetFileReader(teamID, rootID, filename string) (filestore.ReadCloseSeeker, error) { filePath := filepath.Join(teamID, rootID, filename) exists, err := a.filesBackend.FileExists(filePath) diff --git a/server/app/files_test.go b/server/app/files_test.go index 11e4991b8..b39327f7b 100644 --- a/server/app/files_test.go +++ b/server/app/files_test.go @@ -7,6 +7,7 @@ import ( "path/filepath" "strings" "testing" + "time" "github.com/golang/mock/gomock" "github.com/stretchr/testify/assert" @@ -195,8 +196,8 @@ func TestSaveFile(t *testing.T) { writeFileFunc := func(reader io.Reader, path string) int64 { paths := strings.Split(path, string(os.PathSeparator)) - assert.Equal(t, "1", paths[0]) - assert.Equal(t, testBoardID, paths[1]) + assert.Equal(t, "boards", paths[0]) + assert.Equal(t, time.Now().Format("20060102"), paths[1]) fileName = paths[2] return int64(10) } @@ -219,8 +220,8 @@ func TestSaveFile(t *testing.T) { writeFileFunc := func(reader io.Reader, path string) int64 { paths := strings.Split(path, string(os.PathSeparator)) - assert.Equal(t, "1", paths[0]) - assert.Equal(t, "test-board-id", paths[1]) + assert.Equal(t, "boards", paths[0]) + assert.Equal(t, time.Now().Format("20060102"), paths[1]) assert.Equal(t, "jpg", strings.Split(paths[2], ".")[1]) return int64(10) } @@ -243,8 +244,8 @@ func TestSaveFile(t *testing.T) { writeFileFunc := func(reader io.Reader, path string) int64 { paths := strings.Split(path, string(os.PathSeparator)) - assert.Equal(t, "1", paths[0]) - assert.Equal(t, "test-board-id", paths[1]) + assert.Equal(t, "boards", paths[0]) + assert.Equal(t, time.Now().Format("20060102"), paths[1]) assert.Equal(t, "jpg", strings.Split(paths[2], ".")[1]) return int64(10) } @@ -304,3 +305,80 @@ func TestGetFileInfo(t *testing.T) { assert.Nil(t, fetchedFileInfo) }) } + +func TestGetFile(t *testing.T) { + th, _ := SetupTestHelper(t) + + t.Run("when FileInfo exists", func(t *testing.T) { + th.Store.EXPECT().GetFileInfo("fileInfoID").Return(&mmModel.FileInfo{ + Id: "fileInfoID", + Path: "/path/to/file/fileName.txt", + }, nil) + + mockedFileBackend := &mocks.FileBackend{} + th.App.filesBackend = mockedFileBackend + mockedReadCloseSeek := &mocks.ReadCloseSeeker{} + readerFunc := func(path string) filestore.ReadCloseSeeker { + return mockedReadCloseSeek + } + + readerErrorFunc := func(path string) error { + return nil + } + mockedFileBackend.On("Reader", "/path/to/file/fileName.txt").Return(readerFunc, readerErrorFunc) + mockedFileBackend.On("FileExists", "/path/to/file/fileName.txt").Return(true, nil) + + fileInfo, seeker, err := th.App.GetFile("teamID", "boardID", "7fileInfoID.txt") + assert.NoError(t, err) + assert.NotNil(t, fileInfo) + assert.NotNil(t, seeker) + }) + + t.Run("when FileInfo doesn't exist", func(t *testing.T) { + th.Store.EXPECT().GetFileInfo("fileInfoID").Return(nil, nil) + + mockedFileBackend := &mocks.FileBackend{} + th.App.filesBackend = mockedFileBackend + mockedReadCloseSeek := &mocks.ReadCloseSeeker{} + readerFunc := func(path string) filestore.ReadCloseSeeker { + return mockedReadCloseSeek + } + + readerErrorFunc := func(path string) error { + return nil + } + + mockedFileBackend.On("Reader", "teamID/boardID/7fileInfoID.txt").Return(readerFunc, readerErrorFunc) + mockedFileBackend.On("FileExists", "teamID/boardID/7fileInfoID.txt").Return(true, nil) + + fileInfo, seeker, err := th.App.GetFile("teamID", "boardID", "7fileInfoID.txt") + assert.NoError(t, err) + assert.Nil(t, fileInfo) + assert.NotNil(t, seeker) + }) + + t.Run("when FileInfo exists but FileInfo.Path is not set", func(t *testing.T) { + th.Store.EXPECT().GetFileInfo("fileInfoID").Return(&mmModel.FileInfo{ + Id: "fileInfoID", + Path: "", + }, nil) + + mockedFileBackend := &mocks.FileBackend{} + th.App.filesBackend = mockedFileBackend + mockedReadCloseSeek := &mocks.ReadCloseSeeker{} + readerFunc := func(path string) filestore.ReadCloseSeeker { + return mockedReadCloseSeek + } + + readerErrorFunc := func(path string) error { + return nil + } + mockedFileBackend.On("Reader", "teamID/boardID/7fileInfoID.txt").Return(readerFunc, readerErrorFunc) + mockedFileBackend.On("FileExists", "teamID/boardID/7fileInfoID.txt").Return(true, nil) + + fileInfo, seeker, err := th.App.GetFile("teamID", "boardID", "7fileInfoID.txt") + assert.NoError(t, err) + assert.NotNil(t, fileInfo) + assert.NotNil(t, seeker) + }) +} diff --git a/server/client/client.go b/server/client/client.go index 6be594f40..8c794ccfe 100644 --- a/server/client/client.go +++ b/server/client/client.go @@ -380,6 +380,8 @@ func (c *Client) GetCards(boardID string, page int, perPage int) ([]*model.Card, return nil, BuildErrorResponse(r, err) } + defer closeBody(r) + var cards []*model.Card if err := json.NewDecoder(r.Body).Decode(&cards); err != nil { return nil, BuildErrorResponse(r, err) @@ -398,6 +400,8 @@ func (c *Client) PatchCard(cardID string, cardPatch *model.CardPatch, disableNot return nil, BuildErrorResponse(r, err) } + defer closeBody(r) + var cardNew *model.Card if err := json.NewDecoder(r.Body).Decode(&cardNew); err != nil { return nil, BuildErrorResponse(r, err) @@ -412,6 +416,8 @@ func (c *Client) GetCard(cardID string) (*model.Card, *Response) { return nil, BuildErrorResponse(r, err) } + defer closeBody(r) + var card *model.Card if err := json.NewDecoder(r.Body).Decode(&card); err != nil { return nil, BuildErrorResponse(r, err) @@ -450,6 +456,7 @@ func (c *Client) DeleteCategory(teamID, categoryID string) *Response { return BuildErrorResponse(r, err) } + defer closeBody(r) return BuildResponse(r) } @@ -1049,6 +1056,7 @@ func (c *Client) HideBoard(teamID, categoryID, boardID string) *Response { return BuildErrorResponse(r, err) } + defer closeBody(r) return BuildResponse(r) } @@ -1058,5 +1066,6 @@ func (c *Client) UnhideBoard(teamID, categoryID, boardID string) *Response { return BuildErrorResponse(r, err) } + defer closeBody(r) return BuildResponse(r) } diff --git a/server/model/version.go b/server/model/version.go index cace1f357..52fe08e79 100644 --- a/server/model/version.go +++ b/server/model/version.go @@ -8,6 +8,7 @@ import ( // It should be maintained in chronological order with most current // release at the front of the list. var versions = []string{ + "7.10.0", "7.9.0", "7.8.0", "7.7.0", diff --git a/server/services/store/sqlstore/file.go b/server/services/store/sqlstore/file.go index c1e189f3d..5825244c9 100644 --- a/server/services/store/sqlstore/file.go +++ b/server/services/store/sqlstore/file.go @@ -22,6 +22,7 @@ func (s *SQLStore) saveFileInfo(db sq.BaseRunner, fileInfo *mmModel.FileInfo) er "extension", "size", "delete_at", + "path", "archived", ). Values( @@ -31,6 +32,7 @@ func (s *SQLStore) saveFileInfo(db sq.BaseRunner, fileInfo *mmModel.FileInfo) er fileInfo.Extension, fileInfo.Size, fileInfo.DeleteAt, + fileInfo.Path, false, ) @@ -57,6 +59,7 @@ func (s *SQLStore) getFileInfo(db sq.BaseRunner, id string) (*mmModel.FileInfo, "extension", "size", "archived", + "path", ). From(s.tablePrefix + "file_info"). Where(sq.Eq{"Id": id}) @@ -73,6 +76,7 @@ func (s *SQLStore) getFileInfo(db sq.BaseRunner, id string) (*mmModel.FileInfo, &fileInfo.Extension, &fileInfo.Size, &fileInfo.Archived, + &fileInfo.Path, ) if err != nil { diff --git a/server/services/store/sqlstore/migrations/000039_add_path_to_file_info.down.sql b/server/services/store/sqlstore/migrations/000039_add_path_to_file_info.down.sql new file mode 100644 index 000000000..027b7d63f --- /dev/null +++ b/server/services/store/sqlstore/migrations/000039_add_path_to_file_info.down.sql @@ -0,0 +1 @@ +SELECT 1; \ No newline at end of file diff --git a/server/services/store/sqlstore/migrations/000039_add_path_to_file_info.up.sql b/server/services/store/sqlstore/migrations/000039_add_path_to_file_info.up.sql new file mode 100644 index 000000000..af7d5e8fe --- /dev/null +++ b/server/services/store/sqlstore/migrations/000039_add_path_to_file_info.up.sql @@ -0,0 +1 @@ +{{ addColumnIfNeeded "file_info" "path" "varchar(512)" "" }} \ No newline at end of file diff --git a/server/utils/utils.go b/server/utils/utils.go index 7a1ad9763..46326dd66 100644 --- a/server/utils/utils.go +++ b/server/utils/utils.go @@ -2,6 +2,7 @@ package utils import ( "encoding/json" + "path" "reflect" "time" @@ -120,3 +121,7 @@ func DedupeStringArr(arr []string) []string { return dedupedArr } + +func GetBaseFilePath() string { + return path.Join("boards", time.Now().Format("20060102")) +} diff --git a/webapp/i18n/nb_NO.json b/webapp/i18n/nb_NO.json index 3b6c0ce38..604a40885 100644 --- a/webapp/i18n/nb_NO.json +++ b/webapp/i18n/nb_NO.json @@ -1,14 +1,42 @@ { + "AppBar.Tooltip": "Veksle lenkede tavler", + "Attachment.Attachment-title": "Vedlegg", + "AttachmentBlock.DeleteAction": "slett", + "AttachmentBlock.addElement": "legg til {type}", + "AttachmentBlock.delete": "Vedlegg slettet.", + "AttachmentBlock.failed": "Denne filen kunne ikke lastes opp fordi størrelsesgrensen er nådd.", + "AttachmentBlock.upload": "Vedlegg lastes opp.", + "AttachmentBlock.uploadSuccess": "Vedlegg lastet opp.", + "AttachmentElement.delete-confirmation-dialog-button-text": "Slett", + "AttachmentElement.download": "Last ned", + "AttachmentElement.upload-percentage": "Laster opp ...({uploadPercent}%)", "BoardComponent.add-a-group": "+ Legg til gruppe", "BoardComponent.delete": "Slett", "BoardComponent.hidden-columns": "Skjulte kolonner", "BoardComponent.hide": "Skjul", "BoardComponent.new": "+ Ny", "BoardComponent.no-property": "Ingen {property}", - "BoardComponent.no-property-title": "Elementer med en tom {property} område kommer hit. Denne kolonnen kan ikke fjernes.", + "BoardComponent.no-property-title": "Elementer med tom {property} atributt legges her. Denne kolonnen kan ikke fjernes.", "BoardComponent.show": "Vis", + "BoardMember.schemeAdmin": "Admin", + "BoardMember.schemeCommenter": "Kommentator", + "BoardMember.schemeEditor": "Redaktør", + "BoardMember.schemeNone": "Ingen", + "BoardMember.schemeViewer": "Viser", + "BoardMember.unlinkChannel": "Fjern lenke", "BoardPage.newVersion": "En ny versjon av Boards er tilgjengelig, klikk her for å laste inn på nytt.", "BoardPage.syncFailed": "Tavle kan slettes eller adgangen trekkes tilbake.", + "BoardTemplateSelector.add-template": "Lag ny mal", + "BoardTemplateSelector.create-empty-board": "Opprett tom tavle", + "BoardTemplateSelector.delete-template": "Slett", + "BoardTemplateSelector.description": "Legg til en tavle til sidestolpen med hvilken mal du vil fra listen under, eller start med en helt tom tavle.", + "BoardTemplateSelector.edit-template": "Rediger", + "BoardTemplateSelector.plugin.no-content-description": "Legg til en tavle i sidestolpen med hvilken mal du vil, eller start med en tom tavle.", + "BoardTemplateSelector.plugin.no-content-title": "Lag ny tavle", + "BoardTemplateSelector.title": "Lag ny tavle", + "BoardTemplateSelector.use-this-template": "Bruk denne malen", + "BoardsSwitcher.Title": "Finn tavle", + "BoardsUnfurl.Limited": "Flere detaljer er skjult fordi kortet er arkivert", "BoardsUnfurl.Remainder": "+{remainder} mer", "BoardsUnfurl.Updated": "Oppdatert {time}", "Calculations.Options.average.displayName": "Gjennomsnitt", @@ -16,11 +44,142 @@ "Calculations.Options.count.displayName": "Antall", "Calculations.Options.count.label": "Antall", "Calculations.Options.countChecked.displayName": "Avkrysset", - "Calculations.Options.countChecked.label": "Antall avsjekket", + "Calculations.Options.countChecked.label": "Antall valgt", "Calculations.Options.countUnchecked.displayName": "Ikke avmerket", - "Calculations.Options.countUnchecked.label": "Antall Ikke Avmerket", + "Calculations.Options.countUnchecked.label": "Antall ikke valgt", "Calculations.Options.countUniqueValue.displayName": "Unik", - "Calculations.Options.countUniqueValue.label": "Antall Unike Verdier", + "Calculations.Options.countUniqueValue.label": "Antall unike verdier", "Calculations.Options.countValue.displayName": "Verdier", - "Calculations.Options.countValue.label": "Antall Verdier" + "Calculations.Options.countValue.label": "Antall verdier", + "Calculations.Options.dateRange.displayName": "Tidsrom", + "Calculations.Options.dateRange.label": "Tidsrom", + "Calculations.Options.earliest.displayName": "Tiligst", + "Calculations.Options.earliest.label": "Tiligst", + "Calculations.Options.latest.displayName": "Senest", + "Calculations.Options.latest.label": "Senest", + "Calculations.Options.max.displayName": "Maks", + "Calculations.Options.max.label": "Maks", + "Calculations.Options.median.displayName": "Median", + "Calculations.Options.median.label": "Median", + "Calculations.Options.min.displayName": "Min", + "Calculations.Options.min.label": "Min", + "Calculations.Options.none.displayName": "Kalkulèr", + "Calculations.Options.none.label": "Ingen", + "Calculations.Options.percentChecked.displayName": "Valgt", + "Calculations.Options.percentChecked.label": "Prosent valgt", + "Calculations.Options.percentUnchecked.displayName": "Ikke valgt", + "Calculations.Options.percentUnchecked.label": "Prosent ikke valgt", + "Calculations.Options.range.displayName": "Tidsrom", + "Calculations.Options.range.label": "Tidsrom", + "Calculations.Options.sum.displayName": "Sum", + "Calculations.Options.sum.label": "Sum", + "CalendarCard.untitled": "Uten navn", + "CardActionsMenu.copiedLink": "Kopiert!", + "CardActionsMenu.copyLink": "Kopier lenke", + "CardActionsMenu.delete": "Slett", + "CardActionsMenu.duplicate": "Dupliser", + "CardBadges.title-checkboxes": "Avkrysningsbokser", + "CardBadges.title-comments": "Kommentarer", + "CardBadges.title-description": "Dette kortet har en beskrivelsestekst", + "CardDetail.Attach": "Legg ved", + "CardDetail.Follow": "Følg", + "CardDetail.Following": "Følger", + "CardDetail.add-content": "Legg til innhold", + "CardDetail.add-icon": "Legg til ikon", + "CardDetail.add-property": "+ Legg til en verdi", + "CardDetail.addCardText": "legg inn tekst i kortet", + "CardDetail.limited-body": "Oppgrader til vår profesjonelle eller bedriftsplan.", + "CardDetail.limited-button": "Oppgrader", + "CardDetail.limited-title": "Dette kortet er skjult", + "CardDetail.moveContent": "Flytt innholdet", + "CardDetail.new-comment-placeholder": "Legg til kommentar ...", + "CardDetailProperty.confirm-delete-heading": "Bekreft sletting av verdi", + "CardDetailProperty.confirm-delete-subtext": "Er du sikker på at du vil slette verdien \"{propertyName}\"? Dette vil fjerne verdien fra alle kortene på denne tavlen.", + "CardDetailProperty.confirm-property-name-change-subtext": "Er du sikker på at du vil endre verdien \"{propertyName}\" {customText}? Dette vil påvirke verdien på {numOfCards} kort på denne tavlen, og kan forårsake at du mister informasjon.", + "CardDetailProperty.confirm-property-type-change": "Bekreft endring av verditype", + "CardDetailProperty.delete-action-button": "Slett", + "CardDetailProperty.property-change-action-button": "Endre verdi", + "CardDetailProperty.property-changed": "Verdi endret!", + "CardDetailProperty.property-deleted": "Fjernet {propertyName}!", + "CardDetailProperty.property-name-change-subtext": "type fra \"{oldPropType}\" til \"{newPropType}\"", + "CardDetial.limited-link": "Lær mer om våre planer.", + "CardDialog.delete-confirmation-dialog-attachment": "Bekreft sletting av vedlegg", + "CardDialog.delete-confirmation-dialog-button-text": "Slett", + "CardDialog.delete-confirmation-dialog-heading": "Bekreft sletting av kort", + "CardDialog.editing-template": "Du redigerer en mal.", + "CardDialog.nocard": "Dette kortet eksisterer ikke eller du har ikke tilgang.", + "Categories.CreateCategoryDialog.CancelText": "Avbryt", + "Categories.CreateCategoryDialog.CreateText": "Opprett", + "Categories.CreateCategoryDialog.Placeholder": "Navngi kategorien", + "Categories.CreateCategoryDialog.UpdateText": "Oppdater", + "CenterPanel.Login": "Logg inn", + "CenterPanel.Share": "Del", + "ChannelIntro.CreateBoard": "Opprett tavle", + "CloudMessage.cloud-server": "Få din egen gratis skytjener.", + "ColorOption.selectColor": "Velg {color} farge", + "Comment.delete": "Slett", + "CommentsList.send": "Send", + "ConfirmPerson.empty": "Tom", + "ConfirmPerson.search": "Søk ...", + "ConfirmationDialog.cancel-action": "Avbryt", + "ConfirmationDialog.confirm-action": "Bekreft", + "ContentBlock.Delete": "Slett", + "ContentBlock.DeleteAction": "slett", + "ContentBlock.addElement": "legg til {type}", + "ContentBlock.checkbox": "avkrysningsboks", + "ContentBlock.divider": "avdeler", + "ContentBlock.editCardCheckbox": "krysset-boks", + "ContentBlock.editCardCheckboxText": "rediger kort tekst", + "ContentBlock.editCardText": "rediger kort tekst", + "ContentBlock.editText": "Rediger tekst ...", + "ContentBlock.image": "bilde", + "ContentBlock.insertAbove": "Sett inn over", + "ContentBlock.moveBlock": "flytt kort innhold", + "ContentBlock.moveDown": "Flytt ned", + "ContentBlock.moveUp": "Flytt opp", + "ContentBlock.text": "tekst", + "DateRange.clear": "Tøm", + "DateRange.empty": "Tom", + "DateRange.endDate": "Sluttdato", + "DateRange.today": "I dag", + "DeleteBoardDialog.confirm-cancel": "Avbryt", + "DeleteBoardDialog.confirm-delete": "Slett", + "DeleteBoardDialog.confirm-info": "Er du sikker på at du vil slette tavlen \"{boardTitle}\"? Dette vil slette alle kortene på tavlen.", + "DeleteBoardDialog.confirm-info-template": "Er du sikker på at du vil slette tavlemalen \"{boardTitle}\"?", + "DeleteBoardDialog.confirm-tite": "Bekreft sletting av tavle", + "DeleteBoardDialog.confirm-tite-template": "Bekreft sletting av tavlemal", + "Dialog.closeDialog": "Lukk", + "EditableDayPicker.today": "I dag", + "Error.mobileweb": "Støtte for bruk i nettleser på mobil er i tidlig beta. Alt vil ikke fungere.", + "Error.websocket-closed": "Problemer med kobling til tjeneren. Sjekk konfigurasjonen hvis problemet vedvarer.", + "Filter.contains": "inneholder", + "Filter.ends-with": "ender med", + "Filter.includes": "inkluderer", + "Filter.is": "er", + "Filter.is-empty": "er tom", + "Filter.is-not-empty": "er ikke tom", + "Filter.is-not-set": "er ikke satt", + "Filter.is-set": "er satt", + "Filter.not-contains": "inkluderer ikke", + "Filter.not-ends-with": "ender ikke med", + "Filter.not-includes": "inkluderer ikke", + "Filter.not-starts-with": "starter ikke med", + "Filter.starts-with": "starter med", + "FilterByText.placeholder": "filtrer tekst", + "FilterComponent.add-filter": "+ Nytt filter", + "FilterComponent.delete": "Slett", + "FilterValue.empty": "(tom)", + "FindBoardsDialog.IntroText": "Søk etter tavle", + "FindBoardsDialog.NoResultsFor": "Ingen resultat for \"{searchQuery}\"", + "FindBoardsDialog.NoResultsSubtext": "Sjekk stavingen eller søk på noe annet.", + "FindBoardsDialog.SubTitle": "Skriv for å finne en tavle. Bruk opp/ned for å navigere. Enter for å velge, eller Esc for å avbryte", + "FindBoardsDialog.Title": "Finn tavle", + "GroupBy.hideEmptyGroups": "Skjul {count} tomme grupper", + "GroupBy.showHiddenGroups": "Vis {count} tomme grupper", + "GroupBy.ungroup": "Fjern fra gruppe", + "HideBoard.MenuOption": "Skjul tavlen", + "KanbanCard.untitled": "Uten navn", + "Mutator.new-board-from-template": "ny tavle fra mal", + "Mutator.new-card-from-template": "nytt kort fra mal", + "Mutator.new-template-from-card": "ny mal fra kort" } diff --git a/webapp/i18n/pt_BR.json b/webapp/i18n/pt_BR.json index 5cc319d38..aa755dc2f 100644 --- a/webapp/i18n/pt_BR.json +++ b/webapp/i18n/pt_BR.json @@ -1,5 +1,12 @@ { - "AppBar.Tooltip": "Ativar Boards vinculados", + "AppBar.Tooltip": "Ativar boards vinculados", + "Attachment.Attachment-title": "Anexo", + "AttachmentBlock.DeleteAction": "apagar", + "AttachmentBlock.addElement": "adicionar {type}", + "AttachmentBlock.delete": "Anexo apagado.", + "AttachmentBlock.uploadSuccess": "Anexo enviado.", + "AttachmentElement.delete-confirmation-dialog-button-text": "Apagar", + "AttachmentElement.upload-percentage": "Enviando...({uploadPercent}%)", "BoardComponent.add-a-group": "+ Adicione um grupo", "BoardComponent.delete": "Excluir", "BoardComponent.hidden-columns": "Colunas ocultas", @@ -16,8 +23,8 @@ "BoardMember.unlinkChannel": "Desvincular", "BoardPage.newVersion": "Uma nova versão do Boards está disponível, clique aqui para recarregar.", "BoardPage.syncFailed": "O Board pode ter sido excluído ou o acesso revogado.", - "BoardTemplateSelector.add-template": "Novo modelo", - "BoardTemplateSelector.create-empty-board": "Criar board vazio", + "BoardTemplateSelector.add-template": "Criar novo modelo", + "BoardTemplateSelector.create-empty-board": "Criar um board vazio", "BoardTemplateSelector.delete-template": "Excluir", "BoardTemplateSelector.description": "Adicione um quadro à barra lateral usando qualquer um dos modelos definidos abaixo ou comece do zero.", "BoardTemplateSelector.edit-template": "Editar", @@ -25,7 +32,7 @@ "BoardTemplateSelector.plugin.no-content-title": "Criar um board", "BoardTemplateSelector.title": "Criar um board", "BoardTemplateSelector.use-this-template": "Use este template", - "BoardsSwitcher.Title": "Encontrar Boards", + "BoardsSwitcher.Title": "Encontrar boards", "BoardsUnfurl.Limited": "Detalhes adicionais estão ocultos devido ao cartão ter sido arquivado", "BoardsUnfurl.Remainder": "+{remainder} mais", "BoardsUnfurl.Updated": "Atualizado {time}", @@ -71,6 +78,7 @@ "CardBadges.title-checkboxes": "Caixa de seleção", "CardBadges.title-comments": "Comentários", "CardBadges.title-description": "Este cartão tem uma descrição", + "CardDetail.Attach": "Anexar", "CardDetail.Follow": "Seguir", "CardDetail.Following": "Seguindo", "CardDetail.add-content": "Adicionar conteúdo", @@ -102,10 +110,13 @@ "Categories.CreateCategoryDialog.UpdateText": "Atualizar", "CenterPanel.Login": "Login", "CenterPanel.Share": "Compartilhar", + "ChannelIntro.CreateBoard": "Criar um board", "CloudMessage.cloud-server": "Obtenha seu próprio cloud server de graça.", "ColorOption.selectColor": "Selecione {color} Cor", "Comment.delete": "Excluir", "CommentsList.send": "Enviar", + "ConfirmPerson.empty": "Vazio", + "ConfirmPerson.search": "Buscar...", "ConfirmationDialog.cancel-action": "Cancelar", "ConfirmationDialog.confirm-action": "Confirmar", "ContentBlock.Delete": "Excluir", @@ -181,6 +192,7 @@ "OnboardingTour.ShareBoard.Body": "Você pode compartilhar seu board internament, com seu time, ou public para permitir visibilidade fora da sua organização.", "OnboardingTour.ShareBoard.Title": "Compartilhar quadro", "PersonProperty.board-members": "Membros do Board", + "PersonProperty.me": "Eu", "PersonProperty.non-board-members": "Não membros do board", "PropertyMenu.Delete": "Excluir", "PropertyMenu.changeType": "Alterar tipo da propriedade", @@ -235,6 +247,7 @@ "Sidebar.import-archive": "Importar arquivo", "Sidebar.invite-users": "Convidar usuários", "Sidebar.logout": "Sair", + "Sidebar.new-category.badge": "Novo", "Sidebar.no-boards-in-category": "Nenhum board", "Sidebar.product-tour": "Tour pelo produto", "Sidebar.random-icons": "Ícones aleatórios", @@ -257,6 +270,7 @@ "SidebarTour.SidebarCategories.Body": "Todos seus boards agora são organizados sob sua nova barra lateral. Não é mais necessárioa alternar entre espaços de trabalho. Categorias personalizadas em suas estações prévias de trabalho foram automaticamente criadas para você como parte do seu upgrade para v7.2. Estas podem ser removidas ou editadas de acordo com a sua preferência.", "SidebarTour.SidebarCategories.Link": "Saiba mais", "SidebarTour.SidebarCategories.Title": "Categorias de barra lateral", + "SiteStats.total_boards": "Total de boards", "TableComponent.add-icon": "Adicionar Ícone", "TableComponent.name": "Nome", "TableComponent.plus-new": "+ Novo", @@ -267,6 +281,7 @@ "TableHeaderMenu.insert-right": "Inserir à direita", "TableHeaderMenu.sort-ascending": "Ordem ascendente", "TableHeaderMenu.sort-descending": "Ordem descendente", + "TableRow.MoreOption": "Mais ações", "TableRow.open": "Abrir", "TopBar.give-feedback": "Dar feedback", "URLProperty.copiedLink": "Copiado!", @@ -348,6 +363,7 @@ "calendar.month": "Mês", "calendar.today": "HOJE", "calendar.week": "Semana", + "centerPanel.unknown-user": "Usuário desconhecido", "cloudMessage.learn-more": "Saiba mais", "createImageBlock.failed": "Não foi possível enviar o arquivo. Limite de tamanho alcançado.", "default-properties.badges": "Comentários e descrição", @@ -369,6 +385,7 @@ "login.log-in-button": "Entrar", "login.log-in-title": "Entrar", "login.register-button": "ou criar uma conta se você ainda não tiver uma", + "new_channel_modal.create_board.select_template_placeholder": "Selecionar um modelo", "notification-box-card-limit-reached.close-tooltip": "Soneca por 10 dias", "notification-box-card-limit-reached.contact-link": "notificar seu admin", "notification-box-card-limit-reached.link": "Atualizar para um plano pago", @@ -388,14 +405,14 @@ "rhs-boards.dm": "DM", "rhs-boards.gm": "GM", "rhs-boards.header.dm": "esta Direct Message", - "rhs-boards.header.gm": "Este Gruop Message", + "rhs-boards.header.gm": "esta mensagem de grupo", "rhs-boards.last-update-at": "Última atualização em: {datetime}", "rhs-boards.link-boards-to-channel": "Vincular boards para {channelName}", "rhs-boards.linked-boards": "Boards vinculados", "rhs-boards.no-boards-linked-to-channel": "Nenhum board está vinculado a {channelName} ainda", "rhs-boards.no-boards-linked-to-channel-description": "Boards é uma ferramenta de gerenciamento de projeto que ajuda a definir, organizar, rastrear e gerenciar o trabalho entre times, usando uma visualização de quadro estilo Kaban familiar.", "rhs-boards.unlink-board": "Desvincular board", - "rhs-boards.unlink-board1": "Desvincular board Hello", + "rhs-boards.unlink-board1": "Desvincular board", "rhs-channel-boards-header.title": "Boards", "share-board.publish": "Publicar", "share-board.share": "Compartilhar", diff --git a/webapp/i18n/ru.json b/webapp/i18n/ru.json index b2a7419f8..f6b502b65 100644 --- a/webapp/i18n/ru.json +++ b/webapp/i18n/ru.json @@ -2,14 +2,14 @@ "AppBar.Tooltip": "Переключить связанные доски", "Attachment.Attachment-title": "Вложение", "AttachmentBlock.DeleteAction": "Удалить", - "AttachmentBlock.addElement": "добавить", - "AttachmentBlock.delete": "Вложение успешно удалено.", - "AttachmentBlock.failed": "Не удалось загрузить файл. Достигнут предел размера вложения.", + "AttachmentBlock.addElement": "добавить {type}", + "AttachmentBlock.delete": "Вложение удалено.", + "AttachmentBlock.failed": "Не удалось загрузить файл, так как превышена квота на размер файла.", "AttachmentBlock.upload": "Загрузка вложения.", - "AttachmentBlock.uploadSuccess": "Вложение успешно загружено.", + "AttachmentBlock.uploadSuccess": "Вложение загружено.", "AttachmentElement.delete-confirmation-dialog-button-text": "Удалить", "AttachmentElement.download": "Скачать", - "AttachmentElement.upload-percentage": "Загрузка", + "AttachmentElement.upload-percentage": "Загрузка...({uploadPercent}%)", "BoardComponent.add-a-group": "+ Добавить группу", "BoardComponent.delete": "Удалить", "BoardComponent.hidden-columns": "Скрытые столбцы", @@ -31,12 +31,12 @@ "BoardTemplateSelector.delete-template": "Удалить", "BoardTemplateSelector.description": "Добавьте доску на боковую панель, используя любой из шаблонов, описанных ниже, или начните с нуля.", "BoardTemplateSelector.edit-template": "Изменить", - "BoardTemplateSelector.plugin.no-content-description": "Добавьте доску на боковую панель, используя любой из указанных ниже шаблонов, или начните с нуля.{lineBreak} Участники \"{teamName}\" будут иметь доступ к созданным здесь доскам.", + "BoardTemplateSelector.plugin.no-content-description": "Добавьте доску на боковую панель, используя любой из указанных ниже шаблонов, или начните с нуля.", "BoardTemplateSelector.plugin.no-content-title": "Создать доску", "BoardTemplateSelector.title": "Создать доску", "BoardTemplateSelector.use-this-template": "Использовать этот шаблон", "BoardsSwitcher.Title": "Найти доски", - "BoardsUnfurl.Limited": "Информация скрыта в связи с тем, что карточка находится в архиве", + "BoardsUnfurl.Limited": "Информация скрыта, потому что карточка находится в архиве", "BoardsUnfurl.Remainder": "+{remainder} ещё", "BoardsUnfurl.Updated": "Обновлено {time}", "Calculations.Options.average.displayName": "Среднее", @@ -103,9 +103,9 @@ "CardDetailProperty.property-deleted": "{propertyName} успешно удалено!", "CardDetailProperty.property-name-change-subtext": "тип из \"{oldPropType}\" в \"{newPropType}\"", "CardDetial.limited-link": "Узнайте больше о наших планах.", - "CardDialog.delete-confirmation-dialog-attachment": "Подтвердите удаление вложения!", + "CardDialog.delete-confirmation-dialog-attachment": "Подтвердите удаление вложения", "CardDialog.delete-confirmation-dialog-button-text": "Удалить", - "CardDialog.delete-confirmation-dialog-heading": "Подтвердите удаление карточки!", + "CardDialog.delete-confirmation-dialog-heading": "Подтвердите удаление карточки", "CardDialog.editing-template": "Вы редактируете шаблон.", "CardDialog.nocard": "Эта карточка не существует или недоступна.", "Categories.CreateCategoryDialog.CancelText": "Отмена", @@ -114,10 +114,13 @@ "Categories.CreateCategoryDialog.UpdateText": "Обновить", "CenterPanel.Login": "Логин", "CenterPanel.Share": "Поделиться", + "ChannelIntro.CreateBoard": "Создать доску", "CloudMessage.cloud-server": "Получите свой бесплатный облачный сервер.", "ColorOption.selectColor": "Выберите цвет {color}", "Comment.delete": "Удалить", "CommentsList.send": "Отправить", + "ConfirmPerson.empty": "Пусто", + "ConfirmPerson.search": "Поиск...", "ConfirmationDialog.cancel-action": "Отмена", "ConfirmationDialog.confirm-action": "Подтвердить", "ContentBlock.Delete": "Удалить", @@ -165,6 +168,7 @@ "FilterByText.placeholder": "фильтровать текст", "FilterComponent.add-filter": "+ Добавить фильтр", "FilterComponent.delete": "Удалить", + "FilterValue.empty": "(пусто)", "FindBoardsDialog.IntroText": "Поиск досок", "FindBoardsDialog.NoResultsFor": "Нет результатов для \"{searchQuery}\"", "FindBoardsDialog.NoResultsSubtext": "Проверьте правильность написания или попробуйте другой запрос.", @@ -183,7 +187,7 @@ "OnboardingTour.AddComments.Title": "Добавить комментарии", "OnboardingTour.AddDescription.Body": "Добавьте описание к своей карточке, чтобы Ваши коллеги по команде знали, о чем эта карточка.", "OnboardingTour.AddDescription.Title": "Добавить описание", - "OnboardingTour.AddProperties.Body": "Добавляйте различные свойства карточкам, чтобы сделать их более мощными!", + "OnboardingTour.AddProperties.Body": "Добавляйте различные свойства карточкам, чтобы сделать их более значительными.", "OnboardingTour.AddProperties.Title": "Добавить свойства", "OnboardingTour.AddView.Body": "Перейдите сюда, чтобы создать новый вид для организации доски с использованием различных макетов.", "OnboardingTour.AddView.Title": "Добавить новый вид", @@ -284,6 +288,7 @@ "TableHeaderMenu.insert-right": "Вставить справа", "TableHeaderMenu.sort-ascending": "Сортировать по возрастанию", "TableHeaderMenu.sort-descending": "Сортировать по убыванию", + "TableRow.DuplicateCard": "дублировать карточку", "TableRow.MoreOption": "Больше действий", "TableRow.open": "Открыть", "TopBar.give-feedback": "Дать обратную связь", @@ -351,10 +356,13 @@ "WelcomePage.Explore.Button": "Исследовать", "WelcomePage.Heading": "Добро пожаловать на Доски", "WelcomePage.NoThanks.Text": "Нет спасибо, сам разберусь", + "WelcomePage.StartUsingIt.Text": "Начать пользоваться", "Workspace.editing-board-template": "Вы редактируете шаблон доски.", + "badge.guest": "Гость", "boardSelector.confirm-link-board": "Привязать доску к каналу", "boardSelector.confirm-link-board-button": "Да, ссылка доски", - "boardSelector.confirm-link-board-subtext": "Связывание доски \"{boardName}\" с этим каналом даст всем участникам этого канала доступ на редактирование доски. Вы уверены, что хотите связать это?", + "boardSelector.confirm-link-board-subtext": "Связывание доски \"{boardName}\" с каналом даст всем участникам канала доступ на редактирование доски. Вы можете в любое время отвязать доску о канала.", + "boardSelector.confirm-link-board-subtext-with-other-channel": "Привязка \"{boardName}\" с каналом приведет к возможности её редактирования всеми участниками канала (существующими и новыми). Кроме гостей канала.{lineBreak} Эта доска сейчас связана с другим каналом. Он будет отключен, если вы решите изменить привязку.", "boardSelector.create-a-board": "Создать доску", "boardSelector.link": "Ссылка", "boardSelector.search-for-boards": "Поиск досок", @@ -363,6 +371,8 @@ "calendar.month": "Месяц", "calendar.today": "СЕГОДНЯ", "calendar.week": "Неделя", + "centerPanel.undefined": "Отсутствует {propertyName}", + "centerPanel.unknown-user": "Неизвестный пользователь", "cloudMessage.learn-more": "Учить больше", "createImageBlock.failed": "Не удалось загрузить файл. Достигнут предел размера файла.", "default-properties.badges": "Комментарии и описание", @@ -377,11 +387,15 @@ "error.team-undefined": "Не корректная команда.", "error.unknown": "Произошла ошибка.", "generic.previous": "Предыдущий", - "imagePaste.upload-failed": "Некоторые файлы не загружены. Достигнут предел размера файла", + "imagePaste.upload-failed": "Некоторые файлы не загружены из-за превышения квоты на размер файла.", "limitedCard.title": "Карточки скрыты", "login.log-in-button": "Вход в систему", "login.log-in-title": "Вход в систему", "login.register-button": "или создать аккаунт, если у Вас его нет", + "new_channel_modal.create_board.empty_board_description": "Создать новую пустую доску", + "new_channel_modal.create_board.empty_board_title": "Пустая доска", + "new_channel_modal.create_board.select_template_placeholder": "Выбрать шаблон", + "new_channel_modal.create_board.title": "Создать доску для этого канала", "notification-box-card-limit-reached.close-tooltip": "Отложить на 10 дней", "notification-box-card-limit-reached.contact-link": "уведомить Вашего администратора", "notification-box-card-limit-reached.link": "Перейти на платный тариф", @@ -389,13 +403,18 @@ "notification-box-cards-hidden.title": "Это действие скрыло другую карточку", "notification-box.card-limit-reached.not-admin.text": "Чтобы получить доступ к архивным карточкам, Вы можете {contactLink} перейти на платный тариф.", "notification-box.card-limit-reached.text": "Достигнут лимит карточки, чтобы просмотреть старые карточки, {link}", + "person.add-user-to-board": "Добавить {username} на доску", + "person.add-user-to-board-confirm-button": "Добавить доску", + "person.add-user-to-board-permissions": "Разрешения", + "person.add-user-to-board-question": "Вы хотите добавить {username} на доску?", "register.login-button": "или войти в систему, если у вас уже есть аккаунт", "register.signup-title": "Зарегистрируйте свой аккаунт", + "rhs-board-non-admin-msg": "Вы не являетесь администратором этой доски", "rhs-boards.add": "Добавить", "rhs-boards.last-update-at": "Последнее обновление: {datetime}", "rhs-boards.link-boards-to-channel": "Связать доски с {channelName}", "rhs-boards.linked-boards": "Связанные доски", - "rhs-boards.no-boards-linked-to-channel": "К каналу {channelName} пока не подключены доски.", + "rhs-boards.no-boards-linked-to-channel": "К каналу {channelName} пока не подключены доски", "rhs-boards.no-boards-linked-to-channel-description": "Доски — это инструмент управления проектами, который помогает определять, организовывать, отслеживать и управлять работой между командами, используя знакомое представление доски Канбан.", "rhs-boards.unlink-board": "Отвязать доску", "rhs-channel-boards-header.title": "Доски", diff --git a/webapp/package-lock.json b/webapp/package-lock.json index 65f21b94b..9de98ccbd 100644 --- a/webapp/package-lock.json +++ b/webapp/package-lock.json @@ -1,12 +1,12 @@ { "name": "focalboard", - "version": "7.9.0", + "version": "7.10.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "focalboard", - "version": "7.9.0", + "version": "7.10.0", "dependencies": { "@draft-js-plugins/editor": "^4.1.2", "@draft-js-plugins/emoji": "^4.6.0", diff --git a/webapp/package.json b/webapp/package.json index d26d1e3ae..935c76e31 100644 --- a/webapp/package.json +++ b/webapp/package.json @@ -1,6 +1,6 @@ { "name": "focalboard", - "version": "7.9.0", + "version": "7.10.0", "private": true, "description": "", "scripts": { diff --git a/webapp/src/components/globalHeader/__snapshots__/globalHeader.test.tsx.snap b/webapp/src/components/globalHeader/__snapshots__/globalHeader.test.tsx.snap index a348d7b30..ffd6d4819 100644 --- a/webapp/src/components/globalHeader/__snapshots__/globalHeader.test.tsx.snap +++ b/webapp/src/components/globalHeader/__snapshots__/globalHeader.test.tsx.snap @@ -10,7 +10,7 @@ exports[`components/sidebar/GlobalHeader header menu should match snapshot 1`] = /> diff --git a/webapp/src/components/sidebar/__snapshots__/sidebar.test.tsx.snap b/webapp/src/components/sidebar/__snapshots__/sidebar.test.tsx.snap index 8fc1ab28c..4533b2b83 100644 --- a/webapp/src/components/sidebar/__snapshots__/sidebar.test.tsx.snap +++ b/webapp/src/components/sidebar/__snapshots__/sidebar.test.tsx.snap @@ -51,9 +51,9 @@ exports[`components/sidebarSidebar dont show hidden boards 1`] = ` >
- v7.9.0 + v7.10.0
@@ -252,9 +252,9 @@ exports[`components/sidebarSidebar should assign default category if current boa >
- v7.9.0 + v7.10.0
@@ -508,9 +508,9 @@ exports[`components/sidebarSidebar shouldnt do any category assignment is board >
- v7.9.0 + v7.10.0
@@ -919,9 +919,9 @@ exports[`components/sidebarSidebar sidebar hidden 1`] = ` >
- v7.9.0 + v7.10.0
@@ -1213,9 +1213,9 @@ exports[`components/sidebarSidebar some categories hidden 1`] = ` >
- v7.9.0 + v7.10.0
diff --git a/webapp/src/components/table/table.scss b/webapp/src/components/table/table.scss index bbc9ef118..6d36a7215 100644 --- a/webapp/src/components/table/table.scss +++ b/webapp/src/components/table/table.scss @@ -203,6 +203,7 @@ width: inherit; } + .MultiPerson.octo-propertyvalue, .Person.octo-propertyvalue, .DateRange.octo-propertyvalue { overflow: unset; diff --git a/webapp/src/constants.ts b/webapp/src/constants.ts index d900092b1..9331eebe4 100644 --- a/webapp/src/constants.ts +++ b/webapp/src/constants.ts @@ -37,8 +37,8 @@ class Constants { static readonly titleColumnId = '__title' static readonly badgesColumnId = '__badges' - static readonly versionString = '7.9.0' - static readonly versionDisplayString = 'Mar 2023' + static readonly versionString = '7.10.0' + static readonly versionDisplayString = 'Apr 2023' static readonly archiveHelpPage = 'https://docs.mattermost.com/boards/migrate-to-boards.html' static readonly imports = [ diff --git a/webapp/src/pages/welcome/__snapshots__/welcomePage.test.tsx.snap b/webapp/src/pages/welcome/__snapshots__/welcomePage.test.tsx.snap index 2dfc52f90..ef9ac5df4 100644 --- a/webapp/src/pages/welcome/__snapshots__/welcomePage.test.tsx.snap +++ b/webapp/src/pages/welcome/__snapshots__/welcomePage.test.tsx.snap @@ -18,32 +18,40 @@ exports[`pages/welcome Welcome Page shows Explore Page 1`] = ` > Boards is a project management tool that helps define, organize, track, and manage work across teams using a familiar Kanban board view. - Boards Welcome Image - Boards Welcome Image - -
+ Boards Welcome Image +
+ + +
+ @@ -67,32 +75,40 @@ exports[`pages/welcome Welcome Page shows Explore Page with subpath 1`] = ` > Boards is a project management tool that helps define, organize, track, and manage work across teams using a familiar Kanban board view. - Boards Welcome Image - Boards Welcome Image - - + Boards Welcome Image +
+ + +
+ diff --git a/webapp/src/pages/welcome/welcomePage.scss b/webapp/src/pages/welcome/welcomePage.scss index d6810e1bf..ebeb7f2b6 100644 --- a/webapp/src/pages/welcome/welcomePage.scss +++ b/webapp/src/pages/welcome/welcomePage.scss @@ -10,6 +10,26 @@ @media (max-height: 768px) { justify-content: flex-start; height: auto; + padding-top: 40px; + } + + .WelcomePage__content { + display: flex; + flex-direction: column; + align-items: center; + gap: 12px; + + @media (max-height: 800px) { + flex-direction: column-reverse; + margin-top: 16px; + } + } + + .WelcomePage__buttons { + display: flex; + flex-direction: column; + align-items: center; + gap: 12px; } > div { @@ -34,7 +54,6 @@ } .skip { - margin-top: 12px; color: rgba(var(--link-color-rgb), 1); cursor: pointer; diff --git a/webapp/src/pages/welcome/welcomePage.tsx b/webapp/src/pages/welcome/welcomePage.tsx index df7a6edbf..723f5c3f5 100644 --- a/webapp/src/pages/welcome/welcomePage.tsx +++ b/webapp/src/pages/welcome/welcomePage.tsx @@ -127,59 +127,63 @@ const WelcomePage = () => { /> - {/* This image will be rendered on large screens over 2000px */} - Boards Welcome Image +
+ {/* This image will be rendered on large screens over 2000px */} + Boards Welcome Image - {/* This image will be rendered on small screens below 2000px */} - Boards Welcome Image + {/* This image will be rendered on small screens below 2000px */} + Boards Welcome Image - {me?.is_guest !== true && - } +
+ {me?.is_guest !== true && + } - {me?.is_guest !== true && - - - } - {me?.is_guest === true && - } + {me?.is_guest !== true && + + + } + {me?.is_guest === true && + } +
+
) diff --git a/webapp/src/properties/date/__snapshots__/date.test.tsx.snap b/webapp/src/properties/date/__snapshots__/date.test.tsx.snap index 861e203c5..231f47e8a 100644 --- a/webapp/src/properties/date/__snapshots__/date.test.tsx.snap +++ b/webapp/src/properties/date/__snapshots__/date.test.tsx.snap @@ -34,6 +34,23 @@ exports[`properties/dateRange handle clear 1`] = ` `; +exports[`properties/dateRange returns component with new date after prop change 1`] = ` +
+
+ +
+
+`; + exports[`properties/dateRange returns default correctly 1`] = `
{ expect(mockedMutator.changePropertyValue).toHaveBeenCalledWith(board.id, card, propertyTemplate.id, JSON.stringify({from: today})) }) + + test('returns component with new date after prop change', () => { + const component = wrapIntl( + , + ) + + const {container, rerender} = render(component) + + rerender( + wrapIntl( + , + ), + ) + + expect(container).toMatchSnapshot() + }) }) diff --git a/webapp/src/properties/date/date.tsx b/webapp/src/properties/date/date.tsx index 23d764a1b..e2c285b5e 100644 --- a/webapp/src/properties/date/date.tsx +++ b/webapp/src/properties/date/date.tsx @@ -1,6 +1,6 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import React, {useMemo, useState, useCallback} from 'react' +import React, {useMemo, useState, useCallback, useEffect} from 'react' import {useIntl} from 'react-intl' import {DateUtils} from 'react-day-picker' import MomentLocaleUtils from 'react-day-picker/moment' @@ -58,6 +58,12 @@ function DateRange(props: PropertyProps): JSX.Element { const [value, setValue] = useState(propertyValue) const intl = useIntl() + useEffect(() => { + if (value !== propertyValue) { + setValue(propertyValue) + } + }, [propertyValue, setValue]) + const onChange = useCallback((newValue) => { if (value !== newValue) { setValue(newValue) diff --git a/webapp/src/telemetry/telemetryClient.ts b/webapp/src/telemetry/telemetryClient.ts index 460e9d247..c722929ff 100644 --- a/webapp/src/telemetry/telemetryClient.ts +++ b/webapp/src/telemetry/telemetryClient.ts @@ -50,6 +50,7 @@ export const TelemetryActions = { LimitCardLimitReached: 'limit_cardLimitReached', LimitCardLimitLinkOpen: 'limit_cardLimitLinkOpen', VersionMoreInfo: 'version_more_info', + ClickChannelsRHSBoard: 'click_board_in_channels_RHS', } interface IEventProps {