From a229242fcc3fa5db412fa8c52ad5ed3166f6ce35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Magnus=20R=C3=B8ed=20Hestvik?= Date: Tue, 12 Nov 2024 11:05:46 +0100 Subject: [PATCH 01/61] Move old translations to new folder --- packages/bygger/src/AuthenticatedApp.jsx | 2 +- packages/bygger/src/Forms/publish/PublishSettingsModal.tsx | 2 +- packages/bygger/src/context/i18n/I18nContext.tsx | 4 ++-- packages/bygger/src/hooks/useFormioTranslations.ts | 2 +- .../ConfirmDeleteLanguageModal.test.tsx | 0 .../ConfirmDeleteLanguageModal.tsx | 0 .../src/{translations => old_translations}/FormItem.tsx | 0 .../src/{translations => old_translations}/NewTranslation.jsx | 0 .../ObsoleteTranslationsPanel.test.tsx | 0 .../ObsoleteTranslationsPanel.tsx | 0 .../TranslationFormHtmlInput.tsx | 0 .../TranslationFormHtmlSection.tsx | 0 .../TranslationTextInput.jsx | 0 .../TranslationTextInput.test.jsx | 0 .../TranslationsByFormPage.tsx | 0 .../TranslationsByFormRoute.tsx | 0 .../TranslationsFormPage.jsx | 0 .../{translations => old_translations}/TranslationsRouter.jsx | 0 .../global/ApplicationTextTranslationEditPanel.jsx | 0 .../global/ApplicationTextTranslationEditPanel.test.jsx | 0 .../global/GlobalCsvLink.test.tsx | 0 .../global/GlobalCsvLink.tsx | 0 .../global/GlobalTranslationRow.test.tsx | 0 .../global/GlobalTranslationRow.tsx | 0 .../global/GlobalTranslationsPage.jsx | 0 .../global/GlobalTranslationsPage.test.jsx | 0 .../global/GlobalTranslationsPanel.jsx | 0 .../global/PublishGlobalTranslationsButton.tsx | 0 .../global/getCurrenttranslationsReducer.test.ts | 0 .../global/getCurrenttranslationsReducer.ts | 0 .../global/testdata/global-translations.js | 0 .../{translations => old_translations}/global/utils.test.ts | 0 .../src/{translations => old_translations}/global/utils.ts | 0 .../src/{translations => old_translations}/utils.test.ts | 0 .../bygger/src/{translations => old_translations}/utils.ts | 2 +- packages/bygger/src/translations/FormTranslationsPage.tsx | 0 36 files changed, 6 insertions(+), 6 deletions(-) rename packages/bygger/src/{translations => old_translations}/ConfirmDeleteLanguageModal.test.tsx (100%) rename packages/bygger/src/{translations => old_translations}/ConfirmDeleteLanguageModal.tsx (100%) rename packages/bygger/src/{translations => old_translations}/FormItem.tsx (100%) rename packages/bygger/src/{translations => old_translations}/NewTranslation.jsx (100%) rename packages/bygger/src/{translations => old_translations}/ObsoleteTranslationsPanel.test.tsx (100%) rename packages/bygger/src/{translations => old_translations}/ObsoleteTranslationsPanel.tsx (100%) rename packages/bygger/src/{translations => old_translations}/TranslationFormHtmlInput.tsx (100%) rename packages/bygger/src/{translations => old_translations}/TranslationFormHtmlSection.tsx (100%) rename packages/bygger/src/{translations => old_translations}/TranslationTextInput.jsx (100%) rename packages/bygger/src/{translations => old_translations}/TranslationTextInput.test.jsx (100%) rename packages/bygger/src/{translations => old_translations}/TranslationsByFormPage.tsx (100%) rename packages/bygger/src/{translations => old_translations}/TranslationsByFormRoute.tsx (100%) rename packages/bygger/src/{translations => old_translations}/TranslationsFormPage.jsx (100%) rename packages/bygger/src/{translations => old_translations}/TranslationsRouter.jsx (100%) rename packages/bygger/src/{translations => old_translations}/global/ApplicationTextTranslationEditPanel.jsx (100%) rename packages/bygger/src/{translations => old_translations}/global/ApplicationTextTranslationEditPanel.test.jsx (100%) rename packages/bygger/src/{translations => old_translations}/global/GlobalCsvLink.test.tsx (100%) rename packages/bygger/src/{translations => old_translations}/global/GlobalCsvLink.tsx (100%) rename packages/bygger/src/{translations => old_translations}/global/GlobalTranslationRow.test.tsx (100%) rename packages/bygger/src/{translations => old_translations}/global/GlobalTranslationRow.tsx (100%) rename packages/bygger/src/{translations => old_translations}/global/GlobalTranslationsPage.jsx (100%) rename packages/bygger/src/{translations => old_translations}/global/GlobalTranslationsPage.test.jsx (100%) rename packages/bygger/src/{translations => old_translations}/global/GlobalTranslationsPanel.jsx (100%) rename packages/bygger/src/{translations => old_translations}/global/PublishGlobalTranslationsButton.tsx (100%) rename packages/bygger/src/{translations => old_translations}/global/getCurrenttranslationsReducer.test.ts (100%) rename packages/bygger/src/{translations => old_translations}/global/getCurrenttranslationsReducer.ts (100%) rename packages/bygger/src/{translations => old_translations}/global/testdata/global-translations.js (100%) rename packages/bygger/src/{translations => old_translations}/global/utils.test.ts (100%) rename packages/bygger/src/{translations => old_translations}/global/utils.ts (100%) rename packages/bygger/src/{translations => old_translations}/utils.test.ts (100%) rename packages/bygger/src/{translations => old_translations}/utils.ts (99%) create mode 100644 packages/bygger/src/translations/FormTranslationsPage.tsx diff --git a/packages/bygger/src/AuthenticatedApp.jsx b/packages/bygger/src/AuthenticatedApp.jsx index 8b91253d0..a5827530c 100644 --- a/packages/bygger/src/AuthenticatedApp.jsx +++ b/packages/bygger/src/AuthenticatedApp.jsx @@ -4,9 +4,9 @@ import { FormsRouter } from './Forms'; import ImportFormsPage from './import/ImportFormsPage'; import BulkPublishPage from './migration/BulkPublishPage'; import MigrationRouter from './migration/MigrationRouter'; +import TranslationsRouter from './old_translations/TranslationsRouter'; import RecipientsPage from './recipients/RecipientsPage'; import ReportsPage from './reports/ReportsPage'; -import TranslationsRouter from './translations/TranslationsRouter'; function AuthenticatedApp({ serverURL, formio }) { return ( diff --git a/packages/bygger/src/Forms/publish/PublishSettingsModal.tsx b/packages/bygger/src/Forms/publish/PublishSettingsModal.tsx index 19731785a..7456b7201 100644 --- a/packages/bygger/src/Forms/publish/PublishSettingsModal.tsx +++ b/packages/bygger/src/Forms/publish/PublishSettingsModal.tsx @@ -3,7 +3,7 @@ import { ConfirmationModal, makeStyles } from '@navikt/skjemadigitalisering-shar import { FormPropertiesType, I18nTranslations, NavFormType } from '@navikt/skjemadigitalisering-shared-domain'; import { useEffect, useState } from 'react'; import { languagesInNorwegian, useI18nState } from '../../context/i18n'; -import { getFormTexts } from '../../translations/utils'; +import { getFormTexts } from '../../old_translations/utils'; import FormStatus, { determineStatus } from '../status/FormStatus'; import { allLanguagesInNorwegian } from '../status/PublishedLanguages'; import Timestamp from '../status/Timestamp'; diff --git a/packages/bygger/src/context/i18n/I18nContext.tsx b/packages/bygger/src/context/i18n/I18nContext.tsx index 823ba432f..1468e29c2 100644 --- a/packages/bygger/src/context/i18n/I18nContext.tsx +++ b/packages/bygger/src/context/i18n/I18nContext.tsx @@ -1,7 +1,7 @@ import { i18nData, mapTranslationsToFormioI18nObject } from '@navikt/skjemadigitalisering-shared-components'; import { FormioTranslationMap, Language, NavFormType } from '@navikt/skjemadigitalisering-shared-domain'; import React, { createContext, useContext, useEffect, useReducer } from 'react'; -import { getFormTexts } from '../../translations/utils'; +import { getFormTexts } from '../../old_translations/utils'; import i18nReducer, { I18nAction, I18nState } from './i18nReducer'; export const getAvailableLanguages = (translations: FormioTranslationMap) => Object.keys(translations) as Language[]; @@ -26,7 +26,7 @@ const loadTranslationsAndInitState = async ( try { const translations = await loadTranslations(); dispatch({ type: 'init', payload: translations }); - } catch (e) { + } catch (_e) { dispatch({ type: 'error' }); } }; diff --git a/packages/bygger/src/hooks/useFormioTranslations.ts b/packages/bygger/src/hooks/useFormioTranslations.ts index e9f7599d3..19d6d3676 100644 --- a/packages/bygger/src/hooks/useFormioTranslations.ts +++ b/packages/bygger/src/hooks/useFormioTranslations.ts @@ -15,7 +15,7 @@ import { useCallback } from 'react'; import { languagesInNorwegian } from '../context/i18n'; import { combineTranslationResources } from '../context/i18n/translationsMapper'; import { useFeedbackEmit } from '../context/notifications/FeedbackContext'; -import { getTranslationKeysForAllPredefinedTexts, tags } from '../translations/global/utils'; +import { getTranslationKeysForAllPredefinedTexts, tags } from '../old_translations/global/utils'; const { zipCountryNames } = localizationUtils; diff --git a/packages/bygger/src/translations/ConfirmDeleteLanguageModal.test.tsx b/packages/bygger/src/old_translations/ConfirmDeleteLanguageModal.test.tsx similarity index 100% rename from packages/bygger/src/translations/ConfirmDeleteLanguageModal.test.tsx rename to packages/bygger/src/old_translations/ConfirmDeleteLanguageModal.test.tsx diff --git a/packages/bygger/src/translations/ConfirmDeleteLanguageModal.tsx b/packages/bygger/src/old_translations/ConfirmDeleteLanguageModal.tsx similarity index 100% rename from packages/bygger/src/translations/ConfirmDeleteLanguageModal.tsx rename to packages/bygger/src/old_translations/ConfirmDeleteLanguageModal.tsx diff --git a/packages/bygger/src/translations/FormItem.tsx b/packages/bygger/src/old_translations/FormItem.tsx similarity index 100% rename from packages/bygger/src/translations/FormItem.tsx rename to packages/bygger/src/old_translations/FormItem.tsx diff --git a/packages/bygger/src/translations/NewTranslation.jsx b/packages/bygger/src/old_translations/NewTranslation.jsx similarity index 100% rename from packages/bygger/src/translations/NewTranslation.jsx rename to packages/bygger/src/old_translations/NewTranslation.jsx diff --git a/packages/bygger/src/translations/ObsoleteTranslationsPanel.test.tsx b/packages/bygger/src/old_translations/ObsoleteTranslationsPanel.test.tsx similarity index 100% rename from packages/bygger/src/translations/ObsoleteTranslationsPanel.test.tsx rename to packages/bygger/src/old_translations/ObsoleteTranslationsPanel.test.tsx diff --git a/packages/bygger/src/translations/ObsoleteTranslationsPanel.tsx b/packages/bygger/src/old_translations/ObsoleteTranslationsPanel.tsx similarity index 100% rename from packages/bygger/src/translations/ObsoleteTranslationsPanel.tsx rename to packages/bygger/src/old_translations/ObsoleteTranslationsPanel.tsx diff --git a/packages/bygger/src/translations/TranslationFormHtmlInput.tsx b/packages/bygger/src/old_translations/TranslationFormHtmlInput.tsx similarity index 100% rename from packages/bygger/src/translations/TranslationFormHtmlInput.tsx rename to packages/bygger/src/old_translations/TranslationFormHtmlInput.tsx diff --git a/packages/bygger/src/translations/TranslationFormHtmlSection.tsx b/packages/bygger/src/old_translations/TranslationFormHtmlSection.tsx similarity index 100% rename from packages/bygger/src/translations/TranslationFormHtmlSection.tsx rename to packages/bygger/src/old_translations/TranslationFormHtmlSection.tsx diff --git a/packages/bygger/src/translations/TranslationTextInput.jsx b/packages/bygger/src/old_translations/TranslationTextInput.jsx similarity index 100% rename from packages/bygger/src/translations/TranslationTextInput.jsx rename to packages/bygger/src/old_translations/TranslationTextInput.jsx diff --git a/packages/bygger/src/translations/TranslationTextInput.test.jsx b/packages/bygger/src/old_translations/TranslationTextInput.test.jsx similarity index 100% rename from packages/bygger/src/translations/TranslationTextInput.test.jsx rename to packages/bygger/src/old_translations/TranslationTextInput.test.jsx diff --git a/packages/bygger/src/translations/TranslationsByFormPage.tsx b/packages/bygger/src/old_translations/TranslationsByFormPage.tsx similarity index 100% rename from packages/bygger/src/translations/TranslationsByFormPage.tsx rename to packages/bygger/src/old_translations/TranslationsByFormPage.tsx diff --git a/packages/bygger/src/translations/TranslationsByFormRoute.tsx b/packages/bygger/src/old_translations/TranslationsByFormRoute.tsx similarity index 100% rename from packages/bygger/src/translations/TranslationsByFormRoute.tsx rename to packages/bygger/src/old_translations/TranslationsByFormRoute.tsx diff --git a/packages/bygger/src/translations/TranslationsFormPage.jsx b/packages/bygger/src/old_translations/TranslationsFormPage.jsx similarity index 100% rename from packages/bygger/src/translations/TranslationsFormPage.jsx rename to packages/bygger/src/old_translations/TranslationsFormPage.jsx diff --git a/packages/bygger/src/translations/TranslationsRouter.jsx b/packages/bygger/src/old_translations/TranslationsRouter.jsx similarity index 100% rename from packages/bygger/src/translations/TranslationsRouter.jsx rename to packages/bygger/src/old_translations/TranslationsRouter.jsx diff --git a/packages/bygger/src/translations/global/ApplicationTextTranslationEditPanel.jsx b/packages/bygger/src/old_translations/global/ApplicationTextTranslationEditPanel.jsx similarity index 100% rename from packages/bygger/src/translations/global/ApplicationTextTranslationEditPanel.jsx rename to packages/bygger/src/old_translations/global/ApplicationTextTranslationEditPanel.jsx diff --git a/packages/bygger/src/translations/global/ApplicationTextTranslationEditPanel.test.jsx b/packages/bygger/src/old_translations/global/ApplicationTextTranslationEditPanel.test.jsx similarity index 100% rename from packages/bygger/src/translations/global/ApplicationTextTranslationEditPanel.test.jsx rename to packages/bygger/src/old_translations/global/ApplicationTextTranslationEditPanel.test.jsx diff --git a/packages/bygger/src/translations/global/GlobalCsvLink.test.tsx b/packages/bygger/src/old_translations/global/GlobalCsvLink.test.tsx similarity index 100% rename from packages/bygger/src/translations/global/GlobalCsvLink.test.tsx rename to packages/bygger/src/old_translations/global/GlobalCsvLink.test.tsx diff --git a/packages/bygger/src/translations/global/GlobalCsvLink.tsx b/packages/bygger/src/old_translations/global/GlobalCsvLink.tsx similarity index 100% rename from packages/bygger/src/translations/global/GlobalCsvLink.tsx rename to packages/bygger/src/old_translations/global/GlobalCsvLink.tsx diff --git a/packages/bygger/src/translations/global/GlobalTranslationRow.test.tsx b/packages/bygger/src/old_translations/global/GlobalTranslationRow.test.tsx similarity index 100% rename from packages/bygger/src/translations/global/GlobalTranslationRow.test.tsx rename to packages/bygger/src/old_translations/global/GlobalTranslationRow.test.tsx diff --git a/packages/bygger/src/translations/global/GlobalTranslationRow.tsx b/packages/bygger/src/old_translations/global/GlobalTranslationRow.tsx similarity index 100% rename from packages/bygger/src/translations/global/GlobalTranslationRow.tsx rename to packages/bygger/src/old_translations/global/GlobalTranslationRow.tsx diff --git a/packages/bygger/src/translations/global/GlobalTranslationsPage.jsx b/packages/bygger/src/old_translations/global/GlobalTranslationsPage.jsx similarity index 100% rename from packages/bygger/src/translations/global/GlobalTranslationsPage.jsx rename to packages/bygger/src/old_translations/global/GlobalTranslationsPage.jsx diff --git a/packages/bygger/src/translations/global/GlobalTranslationsPage.test.jsx b/packages/bygger/src/old_translations/global/GlobalTranslationsPage.test.jsx similarity index 100% rename from packages/bygger/src/translations/global/GlobalTranslationsPage.test.jsx rename to packages/bygger/src/old_translations/global/GlobalTranslationsPage.test.jsx diff --git a/packages/bygger/src/translations/global/GlobalTranslationsPanel.jsx b/packages/bygger/src/old_translations/global/GlobalTranslationsPanel.jsx similarity index 100% rename from packages/bygger/src/translations/global/GlobalTranslationsPanel.jsx rename to packages/bygger/src/old_translations/global/GlobalTranslationsPanel.jsx diff --git a/packages/bygger/src/translations/global/PublishGlobalTranslationsButton.tsx b/packages/bygger/src/old_translations/global/PublishGlobalTranslationsButton.tsx similarity index 100% rename from packages/bygger/src/translations/global/PublishGlobalTranslationsButton.tsx rename to packages/bygger/src/old_translations/global/PublishGlobalTranslationsButton.tsx diff --git a/packages/bygger/src/translations/global/getCurrenttranslationsReducer.test.ts b/packages/bygger/src/old_translations/global/getCurrenttranslationsReducer.test.ts similarity index 100% rename from packages/bygger/src/translations/global/getCurrenttranslationsReducer.test.ts rename to packages/bygger/src/old_translations/global/getCurrenttranslationsReducer.test.ts diff --git a/packages/bygger/src/translations/global/getCurrenttranslationsReducer.ts b/packages/bygger/src/old_translations/global/getCurrenttranslationsReducer.ts similarity index 100% rename from packages/bygger/src/translations/global/getCurrenttranslationsReducer.ts rename to packages/bygger/src/old_translations/global/getCurrenttranslationsReducer.ts diff --git a/packages/bygger/src/translations/global/testdata/global-translations.js b/packages/bygger/src/old_translations/global/testdata/global-translations.js similarity index 100% rename from packages/bygger/src/translations/global/testdata/global-translations.js rename to packages/bygger/src/old_translations/global/testdata/global-translations.js diff --git a/packages/bygger/src/translations/global/utils.test.ts b/packages/bygger/src/old_translations/global/utils.test.ts similarity index 100% rename from packages/bygger/src/translations/global/utils.test.ts rename to packages/bygger/src/old_translations/global/utils.test.ts diff --git a/packages/bygger/src/translations/global/utils.ts b/packages/bygger/src/old_translations/global/utils.ts similarity index 100% rename from packages/bygger/src/translations/global/utils.ts rename to packages/bygger/src/old_translations/global/utils.ts diff --git a/packages/bygger/src/translations/utils.test.ts b/packages/bygger/src/old_translations/utils.test.ts similarity index 100% rename from packages/bygger/src/translations/utils.test.ts rename to packages/bygger/src/old_translations/utils.test.ts diff --git a/packages/bygger/src/translations/utils.ts b/packages/bygger/src/old_translations/utils.ts similarity index 99% rename from packages/bygger/src/translations/utils.ts rename to packages/bygger/src/old_translations/utils.ts index 6f19b718c..578f188b9 100644 --- a/packages/bygger/src/translations/utils.ts +++ b/packages/bygger/src/old_translations/utils.ts @@ -96,7 +96,7 @@ const getContent = (content: string | undefined): string | undefined => { if (content) { // Formio.js runs code that changes the original text before translating, // and to avoid mismatch in translation object keys we need to do the same. - // @ts-ignore + // @ts-expect-error return NavFormioJs.Utils.translateHTMLTemplate(content, (text) => text); } return content; diff --git a/packages/bygger/src/translations/FormTranslationsPage.tsx b/packages/bygger/src/translations/FormTranslationsPage.tsx new file mode 100644 index 000000000..e69de29bb From d4df01c8241948df92e608e19a0011d7688cc816 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Magnus=20R=C3=B8ed=20Hestvik?= Date: Wed, 13 Nov 2024 13:40:00 +0100 Subject: [PATCH 02/61] backend service --- .../api/formio-global-translations/index.ts | 25 ++++++++++ .../global-translations.ts | 42 ++++++++++++++++ .../forms-api-global-translations/index.ts | 10 ++++ .../routers/api/global-translations/index.ts | 22 -------- .../bygger-backend/src/routers/api/index.ts | 6 ++- .../src/services/GlobalTranslationsService.ts | 50 +++++++++++++++++++ .../src/services/RecipientService.ts | 2 +- packages/bygger-backend/src/services/index.ts | 6 ++- packages/shared-domain/src/index.ts | 2 + .../src/translations/GlobalTranslation.ts | 14 ++++++ 10 files changed, 153 insertions(+), 26 deletions(-) create mode 100644 packages/bygger-backend/src/routers/api/formio-global-translations/index.ts create mode 100644 packages/bygger-backend/src/routers/api/forms-api-global-translations/global-translations.ts create mode 100644 packages/bygger-backend/src/routers/api/forms-api-global-translations/index.ts delete mode 100644 packages/bygger-backend/src/routers/api/global-translations/index.ts create mode 100644 packages/bygger-backend/src/services/GlobalTranslationsService.ts create mode 100644 packages/shared-domain/src/translations/GlobalTranslation.ts diff --git a/packages/bygger-backend/src/routers/api/formio-global-translations/index.ts b/packages/bygger-backend/src/routers/api/formio-global-translations/index.ts new file mode 100644 index 000000000..2de57ac45 --- /dev/null +++ b/packages/bygger-backend/src/routers/api/formio-global-translations/index.ts @@ -0,0 +1,25 @@ +import { Language } from '@navikt/skjemadigitalisering-shared-domain'; +import express, { NextFunction, Request, Response } from 'express'; +import config from '../../../config'; +import { copyService } from '../../../services'; + +const formioGlobalTranslationsRouter = express.Router(); + +formioGlobalTranslationsRouter.put( + '/:language/copy-from-prod', + async (req: Request, res: Response, next: NextFunction) => { + if (config.naisClusterName === 'prod-gcp' || !copyService) { + return res.sendStatus(405); + } + try { + const { language } = req.params; + const formioToken = req.getFormioToken?.(); + await copyService.globalTranslations(language as Language, formioToken); + return res.sendStatus(201); + } catch (error) { + next(error); + } + }, +); + +export default formioGlobalTranslationsRouter; diff --git a/packages/bygger-backend/src/routers/api/forms-api-global-translations/global-translations.ts b/packages/bygger-backend/src/routers/api/forms-api-global-translations/global-translations.ts new file mode 100644 index 000000000..ee78c9ba2 --- /dev/null +++ b/packages/bygger-backend/src/routers/api/forms-api-global-translations/global-translations.ts @@ -0,0 +1,42 @@ +import { FormsApiGlobalTranslation } from '@navikt/skjemadigitalisering-shared-domain'; +import { RequestHandler } from 'express'; +import { globalTranslationsService } from '../../../services'; + +const get: RequestHandler = async (req, res, next) => { + try { + const translations = await globalTranslationsService.get(); + res.json(translations); + } catch (error) { + next(error); + } +}; + +const post: RequestHandler = async (req, res, next) => { + const accessToken = req.headers.AzureAccessToken as string; + try { + const translation = await globalTranslationsService.post(req.body, accessToken); + res.status(201).json(translation); + } catch (error) { + next(error); + } +}; + +const put: RequestHandler = async (req, res, next) => { + const { id } = req.params; + const { revision, nb, nn, en } = req.body as FormsApiGlobalTranslation; + const accessToken = req.headers.AzureAccessToken as string; + const body = { nb, nn, en }; + try { + const translation = await globalTranslationsService.put(id, body, revision!, accessToken); + res.json(translation); + } catch (error) { + next(error); + } +}; + +const globalTranslations = { + get, + post, + put, +}; +export default globalTranslations; diff --git a/packages/bygger-backend/src/routers/api/forms-api-global-translations/index.ts b/packages/bygger-backend/src/routers/api/forms-api-global-translations/index.ts new file mode 100644 index 000000000..953a0d0bd --- /dev/null +++ b/packages/bygger-backend/src/routers/api/forms-api-global-translations/index.ts @@ -0,0 +1,10 @@ +import { Router } from 'express'; +import globalTranslations from './global-translations'; + +const formsApiGlobalTranslationsRouter = Router(); + +formsApiGlobalTranslationsRouter.get('/', globalTranslations.get); +formsApiGlobalTranslationsRouter.post('/', globalTranslations.post); +formsApiGlobalTranslationsRouter.put('/:id', globalTranslations.put); + +export default formsApiGlobalTranslationsRouter; diff --git a/packages/bygger-backend/src/routers/api/global-translations/index.ts b/packages/bygger-backend/src/routers/api/global-translations/index.ts deleted file mode 100644 index f13b9b849..000000000 --- a/packages/bygger-backend/src/routers/api/global-translations/index.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { Language } from '@navikt/skjemadigitalisering-shared-domain'; -import express, { NextFunction, Request, Response } from 'express'; -import config from '../../../config'; -import { copyService } from '../../../services'; - -const globalTranslationsRouter = express.Router(); - -globalTranslationsRouter.put('/:language/copy-from-prod', async (req: Request, res: Response, next: NextFunction) => { - if (config.naisClusterName === 'prod-gcp' || !copyService) { - return res.sendStatus(405); - } - try { - const { language } = req.params; - const formioToken = req.getFormioToken?.(); - await copyService.globalTranslations(language as Language, formioToken); - return res.sendStatus(201); - } catch (error) { - next(error); - } -}); - -export default globalTranslationsRouter; diff --git a/packages/bygger-backend/src/routers/api/index.ts b/packages/bygger-backend/src/routers/api/index.ts index e7cd8476f..49e59a5e2 100644 --- a/packages/bygger-backend/src/routers/api/index.ts +++ b/packages/bygger-backend/src/routers/api/index.ts @@ -6,8 +6,9 @@ import deprecatedPublishForm from './deprecated-publish-form'; import deprecatedUnpublishForm from './deprecated-unpublish-form'; import enhetsliste from './enhetsliste'; import formDiff from './formDiff'; +import formioGlobalTranslationsRouter from './formio-global-translations'; import formsRouter from './forms'; -import globalTranslationsRouter from './global-translations'; +import formsApiGlobalTranslationsRouter from './forms-api-global-translations'; import apiErrorHandler from './helpers/apiErrorHandler'; import authHandlers from './helpers/authHandlers'; import importRouter from './import'; @@ -45,8 +46,9 @@ apiRouter.post('/migrate/update', authorizedPublisher, migrateUpdate); apiRouter.get('/form/:formPath/diff', formDiff); apiRouter.use('/forms', formsRouter); apiRouter.use('/recipients', formsApiAuthHandler, recipientsRouter); +apiRouter.use('/forms-api/global-translations', formsApiAuthHandler, formsApiGlobalTranslationsRouter); apiRouter.use('/import', importRouter); -apiRouter.use('/global-translations', authorizedPublisher, globalTranslationsRouter); +apiRouter.use('/global-translations', authorizedPublisher, formioGlobalTranslationsRouter); apiRouter.post('/log/:level', rateLimiter(60000, 60), log); apiRouter.use(apiErrorHandler); diff --git a/packages/bygger-backend/src/services/GlobalTranslationsService.ts b/packages/bygger-backend/src/services/GlobalTranslationsService.ts new file mode 100644 index 000000000..2f5c6e3cc --- /dev/null +++ b/packages/bygger-backend/src/services/GlobalTranslationsService.ts @@ -0,0 +1,50 @@ +import { FormsApiGlobalTranslation } from '@navikt/skjemadigitalisering-shared-domain'; +import { fetchWithErrorHandling } from '../fetchUtils'; + +export default class GlobalTranslationsService { + readonly formsApiUrl: string; + readonly globalTranslationsPath: string; + + constructor(formsApiUrl: string) { + this.formsApiUrl = formsApiUrl; + this.globalTranslationsPath = '/v1/global-translations'; + } + + createHeaders(accessToken?: string, revisionId?: number) { + return { + 'Content-Type': 'application/json', + ...(accessToken && { Authorization: `Bearer ${accessToken}` }), + ...(revisionId && { 'Formsapi-Entity-Revision': `${revisionId}` }), + }; + } + + async get(): Promise { + const response = await fetchWithErrorHandling(`${this.formsApiUrl}${this.globalTranslationsPath}`, { + headers: this.createHeaders(), + }); + return response.data as FormsApiGlobalTranslation[]; + } + + async post(translation: FormsApiGlobalTranslation, accessToken?: string): Promise { + const response = await fetchWithErrorHandling(`${this.formsApiUrl}${this.globalTranslationsPath}`, { + method: 'POST', + headers: this.createHeaders(accessToken), + body: JSON.stringify(translation), + }); + return response.data as FormsApiGlobalTranslation; + } + + async put( + id: string, + body: Pick, + revision: number, + accessToken: string, + ): Promise { + const response = await fetchWithErrorHandling(`${this.formsApiUrl}${this.globalTranslationsPath}/${id}`, { + method: 'PUT', + headers: this.createHeaders(accessToken, revision), + body: JSON.stringify(body), + }); + return response.data as FormsApiGlobalTranslation; + } +} diff --git a/packages/bygger-backend/src/services/RecipientService.ts b/packages/bygger-backend/src/services/RecipientService.ts index 66cd16533..1cde54572 100644 --- a/packages/bygger-backend/src/services/RecipientService.ts +++ b/packages/bygger-backend/src/services/RecipientService.ts @@ -1,7 +1,7 @@ import { Recipient } from '@navikt/skjemadigitalisering-shared-domain'; import { fetchWithErrorHandling } from '../fetchUtils'; -export class RecipientService { +export default class RecipientService { readonly formsApiUrl: string; readonly recipientsUrl: string; diff --git a/packages/bygger-backend/src/services/index.ts b/packages/bygger-backend/src/services/index.ts index 63c1e10e7..923138d1c 100644 --- a/packages/bygger-backend/src/services/index.ts +++ b/packages/bygger-backend/src/services/index.ts @@ -3,9 +3,10 @@ import config from '../config'; import { getFormioApiProdServiceUrl, getFormioApiServiceUrl } from '../util/formio'; import { createCopyService } from './copy/CopyService'; import { FormioService } from './formioService'; +import GlobalTranslationsService from './GlobalTranslationsService'; import PublisherService from './PublisherService'; import PusherService from './PusherService'; -import { RecipientService } from './RecipientService'; +import RecipientService from './RecipientService'; import ReportService from './ReportService'; const formioApiServiceUrl = getFormioApiServiceUrl(); @@ -15,6 +16,8 @@ const formioService = new FormioService(formioApiServiceUrl); const recipientService = new RecipientService(config.formsApi.url); +const globalTranslationsService = new GlobalTranslationsService(config.formsApi.url); + const backendInstance = new Backend(config, formioService); const publisherService = new PublisherService(formioService, backendInstance); @@ -31,6 +34,7 @@ export { backendInstance, copyService, formioService, + globalTranslationsService, publisherService, pusherService, recipientService, diff --git a/packages/shared-domain/src/index.ts b/packages/shared-domain/src/index.ts index efff3cf79..2bfc79d39 100644 --- a/packages/shared-domain/src/index.ts +++ b/packages/shared-domain/src/index.ts @@ -110,6 +110,7 @@ import MockedComponentObjectForTest from './summary/MockedComponentObjectForTest import formSummaryUtil from './summary/formSummaryUtil'; import { TextSize, TextSizeShort } from './text'; import TEXTS from './texts'; +import { FormsApiGlobalTranslation } from './translations/GlobalTranslation'; import currencyUtils from './utils/currencyUtils'; import dateUtils from './utils/date'; import featureUtils, { FeatureTogglesMap } from './utils/featureUtils'; @@ -189,6 +190,7 @@ export type { FormioTranslationData, FormioTranslationMap, FormioTranslationPayload, + FormsApiGlobalTranslation, FormsResponseForm, ForstesideRecipientAddress, ForstesideRequestBody, diff --git a/packages/shared-domain/src/translations/GlobalTranslation.ts b/packages/shared-domain/src/translations/GlobalTranslation.ts new file mode 100644 index 000000000..709c9c595 --- /dev/null +++ b/packages/shared-domain/src/translations/GlobalTranslation.ts @@ -0,0 +1,14 @@ +import { TranslationTag } from '../languages/types'; + +type FormsApiTranslation = { + id: number; + key: string; + revision?: number; + nb?: string; + nn?: string; + en?: string; + changedAt?: string; + changedBy?: string; +}; + +export type FormsApiGlobalTranslation = FormsApiTranslation & { tag: TranslationTag }; From ed1004faff460175d30f77d2ed313d01d3c36c02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Magnus=20R=C3=B8ed=20Hestvik?= Date: Wed, 13 Nov 2024 16:05:34 +0100 Subject: [PATCH 03/61] New global translations page --- packages/bygger/src/AuthenticatedApp.jsx | 3 + .../src/api/useFormsApiGlobalTranslations.ts | 25 ++++++++ .../Navbar/components/TranslationsMenu.tsx | 18 ++---- .../GlobalTranslationsContext.tsx | 61 +++++++++++++++++++ .../translations/components/ButtonColumn.tsx | 20 ++++++ .../components/TranslationRow.tsx | 18 ++++++ .../components/TranslationTable.tsx | 41 +++++++++++++ .../global/GlobalTranslationsPage.tsx | 41 +++++++++++++ 8 files changed, 213 insertions(+), 14 deletions(-) create mode 100644 packages/bygger/src/api/useFormsApiGlobalTranslations.ts create mode 100644 packages/bygger/src/context/translations/GlobalTranslationsContext.tsx create mode 100644 packages/bygger/src/translations/components/ButtonColumn.tsx create mode 100644 packages/bygger/src/translations/components/TranslationRow.tsx create mode 100644 packages/bygger/src/translations/components/TranslationTable.tsx create mode 100644 packages/bygger/src/translations/global/GlobalTranslationsPage.tsx diff --git a/packages/bygger/src/AuthenticatedApp.jsx b/packages/bygger/src/AuthenticatedApp.jsx index a5827530c..4a08c61cb 100644 --- a/packages/bygger/src/AuthenticatedApp.jsx +++ b/packages/bygger/src/AuthenticatedApp.jsx @@ -7,6 +7,7 @@ import MigrationRouter from './migration/MigrationRouter'; import TranslationsRouter from './old_translations/TranslationsRouter'; import RecipientsPage from './recipients/RecipientsPage'; import ReportsPage from './reports/ReportsPage'; +import GlobalTranslationsPage from './translations/global/GlobalTranslationsPage'; function AuthenticatedApp({ serverURL, formio }) { return ( @@ -14,11 +15,13 @@ function AuthenticatedApp({ serverURL, formio }) { } /> } /> + } /> } /> } /> } /> } /> } /> + } /> } /> diff --git a/packages/bygger/src/api/useFormsApiGlobalTranslations.ts b/packages/bygger/src/api/useFormsApiGlobalTranslations.ts new file mode 100644 index 000000000..5d925f12f --- /dev/null +++ b/packages/bygger/src/api/useFormsApiGlobalTranslations.ts @@ -0,0 +1,25 @@ +import { http as baseHttp, useAppConfig } from '@navikt/skjemadigitalisering-shared-components'; +import { FormsApiGlobalTranslation } from '@navikt/skjemadigitalisering-shared-domain'; +import { useFeedbackEmit } from '../context/notifications/FeedbackContext'; + +const useFormsApiGlobalTranslations = () => { + const feedbackEmit = useFeedbackEmit(); + const appConfig = useAppConfig(); + const http = appConfig.http ?? baseHttp; + const baseUrl = '/api/forms-api/global-translations'; + + const get = async (): Promise => { + try { + return await http.get(baseUrl); + } catch (error) { + const message = (error as Error)?.message; + feedbackEmit.error(`Feil ved henting av mottakere. ${message}`); + } + }; + + return { + get, + }; +}; + +export default useFormsApiGlobalTranslations; diff --git a/packages/bygger/src/components/Navbar/components/TranslationsMenu.tsx b/packages/bygger/src/components/Navbar/components/TranslationsMenu.tsx index 1fa6b0d70..f5096b689 100644 --- a/packages/bygger/src/components/Navbar/components/TranslationsMenu.tsx +++ b/packages/bygger/src/components/Navbar/components/TranslationsMenu.tsx @@ -1,31 +1,21 @@ -import { useLanguageCodeFromURL } from '@navikt/skjemadigitalisering-shared-components'; import { MenuLink } from './MenuLink'; export const TranslationsMenu = () => { - const currentLanguage = useLanguageCodeFromURL(); return ( <> - + Skjematekster - + Grensesnitt - + Statiske tekster - + Validering diff --git a/packages/bygger/src/context/translations/GlobalTranslationsContext.tsx b/packages/bygger/src/context/translations/GlobalTranslationsContext.tsx new file mode 100644 index 000000000..7b60ee51d --- /dev/null +++ b/packages/bygger/src/context/translations/GlobalTranslationsContext.tsx @@ -0,0 +1,61 @@ +import { FormsApiGlobalTranslation, TranslationTag } from '@navikt/skjemadigitalisering-shared-domain'; +import { createContext, useCallback, useContext, useEffect, useMemo, useState } from 'react'; +import useFormsApiGlobalTranslations from '../../api/useFormsApiGlobalTranslations'; + +type TranslationsPerTag = Record; +interface GlobalTranslationsContextValue { + translationsPerTag: TranslationsPerTag; +} + +const defaultValue = { + translationsPerTag: { skjematekster: [], grensesnitt: [], 'statiske-tekster': [], validering: [] }, +}; + +const GlobalTranslationsContext = createContext(defaultValue); + +const GlobalTranslationsProvider = ({ children }) => { + const [formsApiState, setFormsApiState] = useState<{ status: 'init' | 'done'; data?: FormsApiGlobalTranslation[] }>({ + status: 'init', + }); + const translationsApi = useFormsApiGlobalTranslations(); + + const loadTranslations = useCallback(async () => { + if (formsApiState.status === 'init') { + const data = await translationsApi.get(); + setFormsApiState({ status: 'done', data }); + } + }, [formsApiState.status, translationsApi]); + + useEffect(() => { + loadTranslations(); + }, [loadTranslations]); + + const storedTranslationsMap = useMemo(() => { + return formsApiState.data?.reduce((acc, translation) => ({ ...acc, [translation.key]: translation }), []); + }, [formsApiState.data]); + + console.log('storedTranslationsMap', storedTranslationsMap); + + const translationsPerTag: TranslationsPerTag = useMemo(() => { + // const { grensesnitt } = TEXTS; + // const flattenedTexts = flattenTextsForEditPanel(grensesnitt); + // console.log('flattenedTexts', flattenedTexts); + // const predefined = getAllPredefinedOriginalTexts(); + // console.log('predefined', predefined); + // const translationKeys = getTranslationKeysForAllPredefinedTexts(); + // console.log('translationKeys', translationKeys); + + return { + skjematekster: (formsApiState.data ?? []).filter((translation) => translation.tag === 'skjematekster'), + grensesnitt: [], + 'statiske-tekster': [], + validering: [], + }; + }, [formsApiState.data]); + + const value = { translationsPerTag }; + return {children}; +}; + +export const useGlobalTranslations = () => useContext(GlobalTranslationsContext); +export default GlobalTranslationsProvider; diff --git a/packages/bygger/src/translations/components/ButtonColumn.tsx b/packages/bygger/src/translations/components/ButtonColumn.tsx new file mode 100644 index 000000000..1a67b57c6 --- /dev/null +++ b/packages/bygger/src/translations/components/ButtonColumn.tsx @@ -0,0 +1,20 @@ +import { Button, VStack } from '@navikt/ds-react'; +import UserFeedback from '../../components/UserFeedback'; + +const ButtonColumn = () => { + return ( + + + + + + + ); +}; +export default ButtonColumn; diff --git a/packages/bygger/src/translations/components/TranslationRow.tsx b/packages/bygger/src/translations/components/TranslationRow.tsx new file mode 100644 index 000000000..4cdb078ee --- /dev/null +++ b/packages/bygger/src/translations/components/TranslationRow.tsx @@ -0,0 +1,18 @@ +import { Table } from '@navikt/ds-react'; +import { Translation } from './TranslationTable'; + +interface Props { + translation: Translation; +} + +const TranslationRow = ({ translation }: Props) => { + return ( + + {translation.nb} + {translation.nn} + {translation.en} + + ); +}; + +export default TranslationRow; diff --git a/packages/bygger/src/translations/components/TranslationTable.tsx b/packages/bygger/src/translations/components/TranslationTable.tsx new file mode 100644 index 000000000..14d841937 --- /dev/null +++ b/packages/bygger/src/translations/components/TranslationTable.tsx @@ -0,0 +1,41 @@ +import { Table } from '@navikt/ds-react'; +import { SkeletonList } from '@navikt/skjemadigitalisering-shared-components'; +import { useParams } from 'react-router-dom'; +import { useGlobalTranslations } from '../../context/translations/GlobalTranslationsContext'; +import TranslationRow from './TranslationRow'; + +const columns = [ + { key: 'nb-NO', label: 'Bokmål' }, + { key: 'nn-NO', label: 'Nynorsk' }, + { key: 'en', label: 'Engelsk' }, +]; + +const TranslationTable = () => { + const { tag } = useParams(); + const { translationsPerTag } = useGlobalTranslations(); + + if (!tag || !translationsPerTag) { + return ; + } + + return ( + + + + {columns.map((column) => ( + + {column.label} + + ))} + + + + {translationsPerTag[tag].map((row) => ( + + ))} + +
+ ); +}; + +export default TranslationTable; diff --git a/packages/bygger/src/translations/global/GlobalTranslationsPage.tsx b/packages/bygger/src/translations/global/GlobalTranslationsPage.tsx new file mode 100644 index 000000000..16b6a39be --- /dev/null +++ b/packages/bygger/src/translations/global/GlobalTranslationsPage.tsx @@ -0,0 +1,41 @@ +import { useParams } from 'react-router-dom'; +import { AppLayout } from '../../components/AppLayout'; +import RowLayout from '../../components/layout/RowLayout'; +import SidebarLayout from '../../components/layout/SidebarLayout'; +import Title from '../../components/layout/Title'; +import TitleRowLayout from '../../components/layout/TitleRowLayout'; +import GlobalTranslationsProvider from '../../context/translations/GlobalTranslationsContext'; +import ButtonColumn from '../components/ButtonColumn'; +import TranslationTable from '../components/TranslationTable'; + +const titles = { + skjematekster: 'Globale skjematekster', + grensesnitt: 'Globale grensesnittekster', + 'statiske-tekster': 'Globale statiske tekster', + validering: 'Globale valideringstekster', +}; + +const GlobalTranslationsPage = () => { + const { tag = '' } = useParams(); + + return ( + + + {titles[tag]} + + + + + + } + > + + + + + ); +}; + +export default GlobalTranslationsPage; From b55d19991376091805daebebb8a3bf28472fa1ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Magnus=20R=C3=B8ed=20Hestvik?= Date: Mon, 18 Nov 2024 08:54:41 +0100 Subject: [PATCH 04/61] Populate and edit global translations --- .../src/api/useFormsApiGlobalTranslations.ts | 2 +- .../translations/EditTranslationsContext.tsx | 0 .../GlobalTranslationsContext.tsx | 30 +++++++++---- .../components/TranslationInput.tsx | 14 ++++++ .../components/TranslationRow.tsx | 44 ++++++++++++++++--- .../components/TranslationTable.tsx | 6 ++- .../src/translations/components/styles.ts | 10 +++++ .../utils/editGlobalTranslationsUtils.ts | 18 ++++++++ 8 files changed, 107 insertions(+), 17 deletions(-) create mode 100644 packages/bygger/src/context/translations/EditTranslationsContext.tsx create mode 100644 packages/bygger/src/translations/components/TranslationInput.tsx create mode 100644 packages/bygger/src/translations/components/styles.ts create mode 100644 packages/bygger/src/translations/utils/editGlobalTranslationsUtils.ts diff --git a/packages/bygger/src/api/useFormsApiGlobalTranslations.ts b/packages/bygger/src/api/useFormsApiGlobalTranslations.ts index 5d925f12f..ffdad0809 100644 --- a/packages/bygger/src/api/useFormsApiGlobalTranslations.ts +++ b/packages/bygger/src/api/useFormsApiGlobalTranslations.ts @@ -13,7 +13,7 @@ const useFormsApiGlobalTranslations = () => { return await http.get(baseUrl); } catch (error) { const message = (error as Error)?.message; - feedbackEmit.error(`Feil ved henting av mottakere. ${message}`); + feedbackEmit.error(`Feil ved henting av globale oversettelser. ${message}`); } }; diff --git a/packages/bygger/src/context/translations/EditTranslationsContext.tsx b/packages/bygger/src/context/translations/EditTranslationsContext.tsx new file mode 100644 index 000000000..e69de29bb diff --git a/packages/bygger/src/context/translations/GlobalTranslationsContext.tsx b/packages/bygger/src/context/translations/GlobalTranslationsContext.tsx index 7b60ee51d..2b03d4846 100644 --- a/packages/bygger/src/context/translations/GlobalTranslationsContext.tsx +++ b/packages/bygger/src/context/translations/GlobalTranslationsContext.tsx @@ -1,6 +1,7 @@ -import { FormsApiGlobalTranslation, TranslationTag } from '@navikt/skjemadigitalisering-shared-domain'; +import { FormsApiGlobalTranslation, TEXTS, TranslationTag } from '@navikt/skjemadigitalisering-shared-domain'; import { createContext, useCallback, useContext, useEffect, useMemo, useState } from 'react'; import useFormsApiGlobalTranslations from '../../api/useFormsApiGlobalTranslations'; +import { generateAndPopulateTag } from '../../translations/utils/editGlobalTranslationsUtils'; type TranslationsPerTag = Record; interface GlobalTranslationsContextValue { @@ -30,14 +31,14 @@ const GlobalTranslationsProvider = ({ children }) => { loadTranslations(); }, [loadTranslations]); - const storedTranslationsMap = useMemo(() => { - return formsApiState.data?.reduce((acc, translation) => ({ ...acc, [translation.key]: translation }), []); + const storedTranslationsMap = useMemo>(() => { + return (formsApiState.data ?? []).reduce((acc, translation) => ({ ...acc, [translation.key]: translation }), {}); }, [formsApiState.data]); console.log('storedTranslationsMap', storedTranslationsMap); const translationsPerTag: TranslationsPerTag = useMemo(() => { - // const { grensesnitt } = TEXTS; + const { common, grensesnitt, statiske, pdfStatiske, validering } = TEXTS; // const flattenedTexts = flattenTextsForEditPanel(grensesnitt); // console.log('flattenedTexts', flattenedTexts); // const predefined = getAllPredefinedOriginalTexts(); @@ -45,13 +46,26 @@ const GlobalTranslationsProvider = ({ children }) => { // const translationKeys = getTranslationKeysForAllPredefinedTexts(); // console.log('translationKeys', translationKeys); + // const grensesnittTranslations = objectUtils.flattenToArray(grensesnitt, ([entryKey, value], parentKey) => { + // const key = objectUtils.concatKeys(entryKey, parentKey); + // const stored = storedTranslationsMap?.[key]; + // if (stored && stored.tag === 'grensesnitt') { + // return { nb: value, ...stored }; + // } + // return { key, nb: value, tag: 'grensesnitt' }; + // }); + return { skjematekster: (formsApiState.data ?? []).filter((translation) => translation.tag === 'skjematekster'), - grensesnitt: [], - 'statiske-tekster': [], - validering: [], + grensesnitt: generateAndPopulateTag('grensesnitt', { ...common, ...grensesnitt }, storedTranslationsMap), + 'statiske-tekster': generateAndPopulateTag( + 'statiske-tekster', + { ...statiske, pdfStatiske }, + storedTranslationsMap, + ), + validering: generateAndPopulateTag('validering', { validering }, storedTranslationsMap), }; - }, [formsApiState.data]); + }, [formsApiState.data, storedTranslationsMap]); const value = { translationsPerTag }; return {children}; diff --git a/packages/bygger/src/translations/components/TranslationInput.tsx b/packages/bygger/src/translations/components/TranslationInput.tsx new file mode 100644 index 000000000..a83bfaff5 --- /dev/null +++ b/packages/bygger/src/translations/components/TranslationInput.tsx @@ -0,0 +1,14 @@ +import { TextField } from '@navikt/ds-react'; +import { ChangeEventHandler } from 'react'; + +interface Props { + label: string; + defaultValue?: string; + onChange: ChangeEventHandler; +} + +const TranslationInput = ({ label, defaultValue, onChange }: Props) => { + return ; +}; + +export default TranslationInput; diff --git a/packages/bygger/src/translations/components/TranslationRow.tsx b/packages/bygger/src/translations/components/TranslationRow.tsx index 4cdb078ee..a85382c8e 100644 --- a/packages/bygger/src/translations/components/TranslationRow.tsx +++ b/packages/bygger/src/translations/components/TranslationRow.tsx @@ -1,16 +1,48 @@ import { Table } from '@navikt/ds-react'; -import { Translation } from './TranslationTable'; +import { FormsApiGlobalTranslation } from '@navikt/skjemadigitalisering-shared-domain'; +import TranslationInput from './TranslationInput'; +import useTranslationTableStyles from './styles'; interface Props { - translation: Translation; + translation: FormsApiGlobalTranslation; + canEditNB?: boolean; } -const TranslationRow = ({ translation }: Props) => { +const TranslationRow = ({ translation, canEditNB = false }: Props) => { + const styles = useTranslationTableStyles(); + const handleChange = (property: 'nb' | 'nn' | 'en', value: string) => { + console.log('onChange', { ...translation, [property]: value }); + }; + return ( - {translation.nb} - {translation.nn} - {translation.en} + {canEditNB ? ( + + handleChange('nb', event.currentTarget.value)} + /> + + ) : ( + + {translation.nb} + + )} + + handleChange('nn', event.currentTarget.value)} + /> + + + handleChange('en', event.currentTarget.value)} + /> + ); }; diff --git a/packages/bygger/src/translations/components/TranslationTable.tsx b/packages/bygger/src/translations/components/TranslationTable.tsx index 14d841937..099c84cef 100644 --- a/packages/bygger/src/translations/components/TranslationTable.tsx +++ b/packages/bygger/src/translations/components/TranslationTable.tsx @@ -3,6 +3,7 @@ import { SkeletonList } from '@navikt/skjemadigitalisering-shared-components'; import { useParams } from 'react-router-dom'; import { useGlobalTranslations } from '../../context/translations/GlobalTranslationsContext'; import TranslationRow from './TranslationRow'; +import useTranslationTableStyles from './styles'; const columns = [ { key: 'nb-NO', label: 'Bokmål' }, @@ -13,6 +14,7 @@ const columns = [ const TranslationTable = () => { const { tag } = useParams(); const { translationsPerTag } = useGlobalTranslations(); + const styles = useTranslationTableStyles(); if (!tag || !translationsPerTag) { return ; @@ -23,7 +25,7 @@ const TranslationTable = () => { {columns.map((column) => ( - + {column.label} ))} @@ -31,7 +33,7 @@ const TranslationTable = () => { {translationsPerTag[tag].map((row) => ( - + ))} diff --git a/packages/bygger/src/translations/components/styles.ts b/packages/bygger/src/translations/components/styles.ts new file mode 100644 index 000000000..7980143a3 --- /dev/null +++ b/packages/bygger/src/translations/components/styles.ts @@ -0,0 +1,10 @@ +import { makeStyles } from '@navikt/skjemadigitalisering-shared-components'; + +const useTranslationTableStyles = makeStyles({ + column: { + // width: '100%', + maxWidth: '12rem', + }, +}); + +export default useTranslationTableStyles; diff --git a/packages/bygger/src/translations/utils/editGlobalTranslationsUtils.ts b/packages/bygger/src/translations/utils/editGlobalTranslationsUtils.ts new file mode 100644 index 000000000..e4c5c2281 --- /dev/null +++ b/packages/bygger/src/translations/utils/editGlobalTranslationsUtils.ts @@ -0,0 +1,18 @@ +import { FormsApiGlobalTranslation, objectUtils, TranslationTag } from '@navikt/skjemadigitalisering-shared-domain'; + +const generateAndPopulateTag = ( + tagName: TranslationTag, + textObject, + storedTranslationsMap: Record, +) => { + return objectUtils.flattenToArray(textObject, ([entryKey, value], parentKey) => { + const key = objectUtils.concatKeys(entryKey, parentKey); + const stored = storedTranslationsMap?.[key]; + if (stored && stored.tag === tagName) { + return { nb: value, ...stored }; + } + return { key, nb: value, tag: tagName }; + }); +}; + +export { generateAndPopulateTag }; From 8643c9db5bcb241cf9b4f80f69354f22e69c5a58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Magnus=20R=C3=B8ed=20Hestvik?= Date: Tue, 19 Nov 2024 15:50:10 +0100 Subject: [PATCH 05/61] Create new translation and update existing translation --- .../src/api/useFormsApiGlobalTranslations.ts | 22 ++++++++ .../translations/EditTranslationsContext.tsx | 53 +++++++++++++++++++ .../GlobalTranslationsContext.tsx | 44 +++++++++++---- .../translations/components/ButtonColumn.tsx | 17 ++++-- .../components/TranslationInput.tsx | 2 +- .../components/TranslationRow.tsx | 4 ++ .../global/GlobalTranslationsPage.tsx | 21 ++++---- 7 files changed, 140 insertions(+), 23 deletions(-) diff --git a/packages/bygger/src/api/useFormsApiGlobalTranslations.ts b/packages/bygger/src/api/useFormsApiGlobalTranslations.ts index ffdad0809..464221b7d 100644 --- a/packages/bygger/src/api/useFormsApiGlobalTranslations.ts +++ b/packages/bygger/src/api/useFormsApiGlobalTranslations.ts @@ -17,8 +17,30 @@ const useFormsApiGlobalTranslations = () => { } }; + const put = async (translation: FormsApiGlobalTranslation): Promise => { + try { + const { id, revision, nb = null, nn = null, en = null } = translation; + return await http.put(`${baseUrl}/${id}`, { revision, nb, nn, en }); + } catch (error) { + const message = (error as Error)?.message; + feedbackEmit.error(`Feil ved oppdatering av global oversettelse med nøkkel ${translation.key}. ${message}`); + } + }; + + const post = async (translation: FormsApiGlobalTranslation): Promise => { + try { + const { key, tag, nb = null, nn = null, en = null } = translation; + return await http.post(baseUrl, { key, tag, nb, nn, en }); + } catch (error) { + const message = (error as Error)?.message; + feedbackEmit.error(`Feil ved oppretting av global oversettelse med nøkkel ${translation.key}. ${message}`); + } + }; + return { get, + put, + post, }; }; diff --git a/packages/bygger/src/context/translations/EditTranslationsContext.tsx b/packages/bygger/src/context/translations/EditTranslationsContext.tsx index e69de29bb..c122e8fb2 100644 --- a/packages/bygger/src/context/translations/EditTranslationsContext.tsx +++ b/packages/bygger/src/context/translations/EditTranslationsContext.tsx @@ -0,0 +1,53 @@ +import { FormsApiGlobalTranslation } from '@navikt/skjemadigitalisering-shared-domain'; +import { createContext, useContext, useState } from 'react'; +import { useGlobalTranslations } from './GlobalTranslationsContext'; + +//TODO: move me +type TranslationLang = 'nb' | 'nn' | 'en'; + +interface EditTranslationsContextValue { + onTranslationChange: (original: FormsApiGlobalTranslation, property: TranslationLang, value: string) => void; + saveChanges: () => Promise; +} + +const defaultValue = { onTranslationChange: () => {}, saveChanges: () => Promise.resolve() }; + +const EditTranslationsContext = createContext(defaultValue); + +const EditTranslationsProvider = ({ children }) => { + const [changes, setChanges] = useState>(); + const { storedTranslations, saveTranslations } = useGlobalTranslations(); + + const onTranslationChange = ( + originalTranslation: FormsApiGlobalTranslation, + property: 'nb' | 'nn' | 'en', + value: string, + ) => { + setChanges((current) => { + const { key } = originalTranslation; + const storedTranslation = storedTranslations[key]; + if ((storedTranslation?.[property] ?? '') === value) { + return current; + } + console.log('setChange', current, { originalTranslation, property, value }); + const existingChange = current?.[key]; + return { + ...current, + [key]: { ...originalTranslation, ...existingChange, [property]: value }, + }; + }); + }; + + const saveChanges = async () => { + const result = await saveTranslations(Object.values(changes ?? {})); + console.log('saveChanges', result); + setChanges({}); + }; + + const value = { onTranslationChange, saveChanges }; + + return {children}; +}; + +export const useEditTranslations = () => useContext(EditTranslationsContext); +export default EditTranslationsProvider; diff --git a/packages/bygger/src/context/translations/GlobalTranslationsContext.tsx b/packages/bygger/src/context/translations/GlobalTranslationsContext.tsx index 2b03d4846..1a1887f94 100644 --- a/packages/bygger/src/context/translations/GlobalTranslationsContext.tsx +++ b/packages/bygger/src/context/translations/GlobalTranslationsContext.tsx @@ -6,37 +6,61 @@ import { generateAndPopulateTag } from '../../translations/utils/editGlobalTrans type TranslationsPerTag = Record; interface GlobalTranslationsContextValue { translationsPerTag: TranslationsPerTag; + storedTranslations: Record; + saveTranslations: ( + translations: FormsApiGlobalTranslation[], + ) => Promise>; } const defaultValue = { translationsPerTag: { skjematekster: [], grensesnitt: [], 'statiske-tekster': [], validering: [] }, + storedTranslations: {}, + saveTranslations: (_translations: FormsApiGlobalTranslation[]) => Promise.resolve([]), }; const GlobalTranslationsContext = createContext(defaultValue); const GlobalTranslationsProvider = ({ children }) => { - const [formsApiState, setFormsApiState] = useState<{ status: 'init' | 'done'; data?: FormsApiGlobalTranslation[] }>({ + const [formsApiState, setFormsApiState] = useState<{ + status: 'init' | 'ready' | 'saving'; + data?: FormsApiGlobalTranslation[]; + }>({ status: 'init', }); const translationsApi = useFormsApiGlobalTranslations(); const loadTranslations = useCallback(async () => { + const data = await translationsApi.get(); + setFormsApiState({ status: 'ready', data }); + }, [translationsApi]); + + useEffect(() => { if (formsApiState.status === 'init') { - const data = await translationsApi.get(); - setFormsApiState({ status: 'done', data }); + loadTranslations(); } - }, [formsApiState.status, translationsApi]); + }, [formsApiState.status, loadTranslations]); - useEffect(() => { - loadTranslations(); - }, [loadTranslations]); + const saveTranslations = async ( + translations: FormsApiGlobalTranslation[], + ): Promise> => { + setFormsApiState((state) => ({ ...state, status: 'saving' })); + const result = await Promise.all( + translations.map((translation) => { + if (translation.id) { + return translationsApi.put(translation); + } else { + return translationsApi.post(translation); + } + }), + ); + await loadTranslations(); + return result; + }; const storedTranslationsMap = useMemo>(() => { return (formsApiState.data ?? []).reduce((acc, translation) => ({ ...acc, [translation.key]: translation }), {}); }, [formsApiState.data]); - console.log('storedTranslationsMap', storedTranslationsMap); - const translationsPerTag: TranslationsPerTag = useMemo(() => { const { common, grensesnitt, statiske, pdfStatiske, validering } = TEXTS; // const flattenedTexts = flattenTextsForEditPanel(grensesnitt); @@ -67,7 +91,7 @@ const GlobalTranslationsProvider = ({ children }) => { }; }, [formsApiState.data, storedTranslationsMap]); - const value = { translationsPerTag }; + const value = { translationsPerTag, storedTranslations: storedTranslationsMap, saveTranslations }; return {children}; }; diff --git a/packages/bygger/src/translations/components/ButtonColumn.tsx b/packages/bygger/src/translations/components/ButtonColumn.tsx index 1a67b57c6..b70a0f516 100644 --- a/packages/bygger/src/translations/components/ButtonColumn.tsx +++ b/packages/bygger/src/translations/components/ButtonColumn.tsx @@ -1,16 +1,27 @@ import { Button, VStack } from '@navikt/ds-react'; +import { useState } from 'react'; import UserFeedback from '../../components/UserFeedback'; +import { useEditTranslations } from '../../context/translations/EditTranslationsContext'; const ButtonColumn = () => { + const [isSaving, setIsSaving] = useState(false); + const { saveChanges } = useEditTranslations(); + + const onSave = async () => { + setIsSaving(true); + await saveChanges(); + setIsSaving(false); + }; + return ( - - - diff --git a/packages/bygger/src/translations/components/TranslationInput.tsx b/packages/bygger/src/translations/components/TranslationInput.tsx index a83bfaff5..fb115cb1a 100644 --- a/packages/bygger/src/translations/components/TranslationInput.tsx +++ b/packages/bygger/src/translations/components/TranslationInput.tsx @@ -8,7 +8,7 @@ interface Props { } const TranslationInput = ({ label, defaultValue, onChange }: Props) => { - return ; + return ; }; export default TranslationInput; diff --git a/packages/bygger/src/translations/components/TranslationRow.tsx b/packages/bygger/src/translations/components/TranslationRow.tsx index a85382c8e..15d551d16 100644 --- a/packages/bygger/src/translations/components/TranslationRow.tsx +++ b/packages/bygger/src/translations/components/TranslationRow.tsx @@ -1,5 +1,6 @@ import { Table } from '@navikt/ds-react'; import { FormsApiGlobalTranslation } from '@navikt/skjemadigitalisering-shared-domain'; +import { useEditTranslations } from '../../context/translations/EditTranslationsContext'; import TranslationInput from './TranslationInput'; import useTranslationTableStyles from './styles'; @@ -9,8 +10,11 @@ interface Props { } const TranslationRow = ({ translation, canEditNB = false }: Props) => { + const { onTranslationChange } = useEditTranslations(); const styles = useTranslationTableStyles(); + const handleChange = (property: 'nb' | 'nn' | 'en', value: string) => { + onTranslationChange(translation, property, value); console.log('onChange', { ...translation, [property]: value }); }; diff --git a/packages/bygger/src/translations/global/GlobalTranslationsPage.tsx b/packages/bygger/src/translations/global/GlobalTranslationsPage.tsx index 16b6a39be..dd2dfa0f1 100644 --- a/packages/bygger/src/translations/global/GlobalTranslationsPage.tsx +++ b/packages/bygger/src/translations/global/GlobalTranslationsPage.tsx @@ -4,6 +4,7 @@ import RowLayout from '../../components/layout/RowLayout'; import SidebarLayout from '../../components/layout/SidebarLayout'; import Title from '../../components/layout/Title'; import TitleRowLayout from '../../components/layout/TitleRowLayout'; +import EditTranslationsProvider from '../../context/translations/EditTranslationsContext'; import GlobalTranslationsProvider from '../../context/translations/GlobalTranslationsContext'; import ButtonColumn from '../components/ButtonColumn'; import TranslationTable from '../components/TranslationTable'; @@ -24,15 +25,17 @@ const GlobalTranslationsPage = () => { {titles[tag]} - - - - } - > - - + + + + + } + > + + + ); From 8379c9a20479c64f7f1f3dce440375ec0e270c58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Magnus=20R=C3=B8ed=20Hestvik?= Date: Wed, 20 Nov 2024 11:52:33 +0100 Subject: [PATCH 06/61] Use TextArea for longer inputs --- .../components/TranslationInput.tsx | 21 +++++++++++++++---- .../components/TranslationRow.tsx | 6 ++++++ .../utils/editGlobalTranslationsUtils.ts | 6 +++++- 3 files changed, 28 insertions(+), 5 deletions(-) diff --git a/packages/bygger/src/translations/components/TranslationInput.tsx b/packages/bygger/src/translations/components/TranslationInput.tsx index fb115cb1a..9bc8f9d65 100644 --- a/packages/bygger/src/translations/components/TranslationInput.tsx +++ b/packages/bygger/src/translations/components/TranslationInput.tsx @@ -1,13 +1,26 @@ -import { TextField } from '@navikt/ds-react'; -import { ChangeEventHandler } from 'react'; +import { TextField, Textarea } from '@navikt/ds-react'; +import { FocusEventHandler } from 'react'; interface Props { label: string; defaultValue?: string; - onChange: ChangeEventHandler; + minRows: number; + onChange: FocusEventHandler; } -const TranslationInput = ({ label, defaultValue, onChange }: Props) => { +const TranslationInput = ({ label, defaultValue, minRows, onChange }: Props) => { + if (minRows > 2) { + return ( +