From 035ed6fee36b41adbadc527433f43b0e51641245 Mon Sep 17 00:00:00 2001 From: Ali BARIN Date: Mon, 10 Mar 2025 09:34:41 +0000 Subject: [PATCH 1/5] feat(config): add customizable footer --- .../api/v1/admin/config/update.ee.js | 18 +++++++ .../api/v1/admin/config/update.ee.test.js | 21 ++++++++ .../api/v1/automatisch/config.ee.test.js | 19 +++++++ ...0304095143_add_footer_columns_in_config.js | 27 ++++++++++ .../models/__snapshots__/config.test.js.snap | 51 +++++++++++++++++++ packages/backend/src/models/config.js | 9 ++++ packages/backend/src/serializers/config.js | 9 ++++ .../backend/src/serializers/config.test.js | 9 ++++ .../mocks/rest/api/v1/automatisch/config.js | 9 ++++ 9 files changed, 172 insertions(+) create mode 100644 packages/backend/src/db/migrations/20250304095143_add_footer_columns_in_config.js diff --git a/packages/backend/src/controllers/api/v1/admin/config/update.ee.js b/packages/backend/src/controllers/api/v1/admin/config/update.ee.js index 50c76beb..47a5b6ec 100644 --- a/packages/backend/src/controllers/api/v1/admin/config/update.ee.js +++ b/packages/backend/src/controllers/api/v1/admin/config/update.ee.js @@ -11,6 +11,15 @@ export default async (request, response) => { const configParams = (request) => { const { + enableFooter, + footerBackgroundColor, + footerCopyrightText, + footerDocsLink, + footerImprintLink, + footerLogoSvgData, + footerPrivacyPolicyLink, + footerTextColor, + footerTosLink, logoSvgData, palettePrimaryDark, palettePrimaryLight, @@ -19,6 +28,15 @@ const configParams = (request) => { } = request.body; return { + enableFooter, + footerBackgroundColor, + footerCopyrightText, + footerDocsLink, + footerImprintLink, + footerLogoSvgData, + footerPrivacyPolicyLink, + footerTextColor, + footerTosLink, logoSvgData, palettePrimaryDark, palettePrimaryLight, diff --git a/packages/backend/src/controllers/api/v1/admin/config/update.ee.test.js b/packages/backend/src/controllers/api/v1/admin/config/update.ee.test.js index 5984cdbb..11d65ddc 100644 --- a/packages/backend/src/controllers/api/v1/admin/config/update.ee.test.js +++ b/packages/backend/src/controllers/api/v1/admin/config/update.ee.test.js @@ -25,6 +25,18 @@ describe('PATCH /api/v1/admin/config', () => { const palettePrimaryMain = '#00adef'; const palettePrimaryDark = '#222222'; const palettePrimaryLight = '#f90707'; + const enableFooter = true; + const footerCopyrightText = '© AB Software GmbH'; + const footerBackgroundColor = '#FFFFFF'; + const footerTextColor = '#000000'; + const footerDocsLink = 'https://automatisch.io/docs'; + const footerTosLink = 'https://automatisch.io/terms'; + const footerPrivacyPolicyLink = 'https://automatisch.io/privacy'; + const footerImprintLink = 'https://automatisch.io/imprint'; + + const footerLogoSvgData = + 'Sample Footer Logo'; + const logoSvgData = 'A'; @@ -34,6 +46,15 @@ describe('PATCH /api/v1/admin/config', () => { palettePrimaryDark: palettePrimaryDark, palettePrimaryLight: palettePrimaryLight, logoSvgData: logoSvgData, + enableFooter, + footerCopyrightText, + footerBackgroundColor, + footerTextColor, + footerDocsLink, + footerTosLink, + footerPrivacyPolicyLink, + footerImprintLink, + footerLogoSvgData, }; await updateConfig(appConfig); diff --git a/packages/backend/src/controllers/api/v1/automatisch/config.ee.test.js b/packages/backend/src/controllers/api/v1/automatisch/config.ee.test.js index 01ca3365..56a70125 100644 --- a/packages/backend/src/controllers/api/v1/automatisch/config.ee.test.js +++ b/packages/backend/src/controllers/api/v1/automatisch/config.ee.test.js @@ -28,6 +28,15 @@ describe('GET /api/v1/automatisch/config', () => { palettePrimaryMain: '#0059F7', title: 'Sample Title', enableTemplates: true, + enableFooter: true, + footerLogoSvgData: 'Sample Footer Logo', + footerCopyrightText: '© AB Software GmbH', + footerBackgroundColor: '#FFFFFF', + footerTextColor: '#000000', + footerDocsLink: 'https://automatisch.io/docs', + footerTosLink: 'https://automatisch.io/terms', + footerPrivacyPolicyLink: 'https://automatisch.io/privacy', + footerImprintLink: 'https://automatisch.io/imprint', }); const response = await request(app) @@ -41,6 +50,16 @@ describe('GET /api/v1/automatisch/config', () => { additionalDrawerLink: 'link', additionalDrawerLinkIcon: 'icon', additionalDrawerLinkText: 'text', + enableTemplates: true, + enableFooter: true, + footerLogoSvgData: 'Sample Footer Logo', + footerCopyrightText: '© AB Software GmbH', + footerBackgroundColor: '#FFFFFF', + footerTextColor: '#000000', + footerDocsLink: 'https://automatisch.io/docs', + footerTosLink: 'https://automatisch.io/terms', + footerPrivacyPolicyLink: 'https://automatisch.io/privacy', + footerImprintLink: 'https://automatisch.io/imprint', }); expect(response.body).toStrictEqual(expectedPayload); diff --git a/packages/backend/src/db/migrations/20250304095143_add_footer_columns_in_config.js b/packages/backend/src/db/migrations/20250304095143_add_footer_columns_in_config.js new file mode 100644 index 00000000..bff24b62 --- /dev/null +++ b/packages/backend/src/db/migrations/20250304095143_add_footer_columns_in_config.js @@ -0,0 +1,27 @@ +export async function up(knex) { + await knex.schema.table('config', (table) => { + table.boolean('enable_footer').defaultTo(false); + table.text('footer_logo_svg_data'); + table.text('footer_copyright_text'); + table.text('footer_background_color'); + table.text('footer_text_color'); + table.text('footer_docs_link'); + table.text('footer_tos_link'); + table.text('footer_privacy_policy_link'); + table.text('footer_imprint_link'); + }); +} + +export async function down(knex) { + await knex.schema.table('config', (table) => { + table.dropColumn('enable_footer'); + table.dropColumn('footer_copyright_text'); + table.dropColumn('footer_logo_svg_data'); + table.dropColumn('footer_background_color'); + table.dropColumn('footer_text_color'); + table.dropColumn('footer_docs_link'); + table.dropColumn('footer_tos_link'); + table.dropColumn('footer_privacy_policy_link'); + table.dropColumn('footer_imprint_link'); + }); +} diff --git a/packages/backend/src/models/__snapshots__/config.test.js.snap b/packages/backend/src/models/__snapshots__/config.test.js.snap index b3650d4a..c91ed9c6 100644 --- a/packages/backend/src/models/__snapshots__/config.test.js.snap +++ b/packages/backend/src/models/__snapshots__/config.test.js.snap @@ -6,12 +6,63 @@ exports[`Config model > jsonSchema should have correct validations 1`] = ` "createdAt": { "type": "string", }, + "enableFooter": { + "type": "boolean", + }, "enableTemplates": { "type": [ "boolean", "null", ], }, + "footerBackgroundColor": { + "type": [ + "string", + "null", + ], + }, + "footerCopyrightText": { + "type": [ + "string", + "null", + ], + }, + "footerDocsLink": { + "type": [ + "string", + "null", + ], + }, + "footerImprintLink": { + "type": [ + "string", + "null", + ], + }, + "footerLogoSvgData": { + "type": [ + "string", + "null", + ], + }, + "footerPrivacyPolicyLink": { + "type": [ + "string", + "null", + ], + }, + "footerTextColor": { + "type": [ + "string", + "null", + ], + }, + "footerTosLink": { + "type": [ + "string", + "null", + ], + }, "id": { "format": "uuid", "type": "string", diff --git a/packages/backend/src/models/config.js b/packages/backend/src/models/config.js index 7fda0860..9254ed76 100644 --- a/packages/backend/src/models/config.js +++ b/packages/backend/src/models/config.js @@ -16,6 +16,15 @@ class Config extends Base { palettePrimaryMain: { type: ['string', 'null'] }, title: { type: ['string', 'null'] }, enableTemplates: { type: ['boolean', 'null'] }, + enableFooter: { type: 'boolean' }, + footerLogoSvgData: { type: ['string', 'null'] }, + footerCopyrightText: { type: ['string', 'null'] }, + footerBackgroundColor: { type: ['string', 'null'] }, + footerTextColor: { type: ['string', 'null'] }, + footerDocsLink: { type: ['string', 'null'] }, + footerTosLink: { type: ['string', 'null'] }, + footerPrivacyPolicyLink: { type: ['string', 'null'] }, + footerImprintLink: { type: ['string', 'null'] }, createdAt: { type: 'string' }, updatedAt: { type: 'string' }, }, diff --git a/packages/backend/src/serializers/config.js b/packages/backend/src/serializers/config.js index 44308938..fb8180b1 100644 --- a/packages/backend/src/serializers/config.js +++ b/packages/backend/src/serializers/config.js @@ -15,6 +15,15 @@ const configSerializer = (config) => { palettePrimaryLight: config.palettePrimaryLight, installationCompleted: config.installationCompleted, title: config.title, + enableFooter: config.enableFooter, + footerLogoSvgData: config.footerLogoSvgData, + footerCopyrightText: config.footerCopyrightText, + footerBackgroundColor: config.footerBackgroundColor, + footerTextColor: config.footerTextColor, + footerDocsLink: config.footerDocsLink, + footerTosLink: config.footerTosLink, + footerPrivacyPolicyLink: config.footerPrivacyPolicyLink, + footerImprintLink: config.footerImprintLink, }; }; diff --git a/packages/backend/src/serializers/config.test.js b/packages/backend/src/serializers/config.test.js index 025f286a..ec37a3d0 100644 --- a/packages/backend/src/serializers/config.test.js +++ b/packages/backend/src/serializers/config.test.js @@ -24,6 +24,15 @@ describe('configSerializer', () => { additionalDrawerLink: config.additionalDrawerLink, additionalDrawerLinkIcon: config.additionalDrawerLinkIcon, additionalDrawerLinkText: config.additionalDrawerLinkText, + enableFooter: config.enableFooter, + footerBackgroundColor: config.footerBackgroundColor, + footerCopyrightText: config.footerCopyrightText, + footerDocsLink: config.footerDocsLink, + footerImprintLink: config.footerImprintLink, + footerLogoSvgData: config.footerLogoSvgData, + footerPrivacyPolicyLink: config.footerPrivacyPolicyLink, + footerTextColor: config.footerTextColor, + footerTosLink: config.footerTosLink, createdAt: config.createdAt.getTime(), updatedAt: config.updatedAt.getTime(), }; diff --git a/packages/backend/test/mocks/rest/api/v1/automatisch/config.js b/packages/backend/test/mocks/rest/api/v1/automatisch/config.js index eae9202c..04d4a35e 100644 --- a/packages/backend/test/mocks/rest/api/v1/automatisch/config.js +++ b/packages/backend/test/mocks/rest/api/v1/automatisch/config.js @@ -16,6 +16,15 @@ const configMock = (config) => { installationCompleted: config.installationCompleted || false, title: config.title, enableTemplates: config.enableTemplates, + enableFooter: config.enableFooter || false, + footerLogoSvgData: config.footerLogoSvgData, + footerCopyrightText: config.footerCopyrightText, + footerBackgroundColor: config.footerBackgroundColor, + footerTextColor: config.footerTextColor, + footerDocsLink: config.footerDocsLink, + footerTosLink: config.footerTosLink, + footerPrivacyPolicyLink: config.footerPrivacyPolicyLink, + footerImprintLink: config.footerImprintLink, }, meta: { count: 1, From 91fd10ebda13d9dcfbadf866c65e1b38cd9b23dd Mon Sep 17 00:00:00 2001 From: Ali BARIN Date: Mon, 10 Mar 2025 09:36:40 +0000 Subject: [PATCH 2/5] feat(Layout): add custom footer --- packages/web/public/index.html | 9 +- .../web/src/components/CustomLogo/style.ee.js | 1 + .../src/components/Layout/Footer/index.jsx | 126 ++++++++++++++++++ packages/web/src/components/Layout/index.jsx | 3 + .../src/components/ThemeProvider/index.jsx | 12 ++ packages/web/src/styles/theme.js | 7 + 6 files changed, 157 insertions(+), 1 deletion(-) create mode 100644 packages/web/src/components/Layout/Footer/index.jsx diff --git a/packages/web/public/index.html b/packages/web/public/index.html index 26c342fd..3c6345fb 100644 --- a/packages/web/public/index.html +++ b/packages/web/public/index.html @@ -1,4 +1,4 @@ - + @@ -25,6 +25,7 @@ crossorigin type="font/ttf" /> + + + diff --git a/packages/web/src/components/CustomLogo/style.ee.js b/packages/web/src/components/CustomLogo/style.ee.js index e38a9691..fd2d15be 100644 --- a/packages/web/src/components/CustomLogo/style.ee.js +++ b/packages/web/src/components/CustomLogo/style.ee.js @@ -1,4 +1,5 @@ import styled from '@emotion/styled'; + export const LogoImage = styled('img')(() => ({ maxWidth: 200, maxHeight: 22, diff --git a/packages/web/src/components/Layout/Footer/index.jsx b/packages/web/src/components/Layout/Footer/index.jsx new file mode 100644 index 00000000..ef0d3f28 --- /dev/null +++ b/packages/web/src/components/Layout/Footer/index.jsx @@ -0,0 +1,126 @@ +import styled from '@emotion/styled'; +import Box from '@mui/material/Box'; +import Divider from '@mui/material/Divider'; +import Link from '@mui/material/Link'; +import Stack from '@mui/material/Stack'; +import SvgIcon from '@mui/material/SvgIcon'; +import Typography from '@mui/material/Typography'; + +import useAutomatischConfig from 'hooks/useAutomatischConfig'; +import useFormatMessage from 'hooks/useFormatMessage'; +import useVersion from 'hooks/useVersion'; + +const LogoImage = styled('img')(() => ({ + maxWidth: 'auto', + height: 22, +})); + +const LayoutFooter = () => { + const { data: config, isPending: isConfigPending } = useAutomatischConfig(); + const formatMessage = useFormatMessage(); + + const links = [ + { + key: 'docs', + show: !!config.data.footerDocsLink, + href: config.data.footerDocsLink, + text: formatMessage('footer.docsLinkText'), + }, + { + key: 'terms-of-services', + show: !!config.data.footerTosLink, + href: config.data.footerTosLink, + text: formatMessage('footer.tosLinkText'), + }, + + { + key: 'privacy-policy', + show: !!config.data.footerPrivacyPolicyLink, + href: config.data.footerPrivacyPolicyLink, + text: formatMessage('footer.privacyPolicyLinkText'), + }, + + { + key: 'imprint', + show: !!config.data.footerImprintLink, + href: config.data.footerImprintLink, + text: formatMessage('footer.imprintLinkText'), + }, + , + ]; + + if (config.data.enableFooter !== true) return null; + + return ( + + + + + theme.palette.footer.main, + color: (theme) => theme.palette.footer.text, + }} + > +
+ {config.data.footerLogoSvgData && ( + <> + + + )} +
+ +
+ {config.data.footerCopyrightText && ( + + {config.data.footerCopyrightText} + + )} +
+ + + {links + .filter((link) => link.show) + .map((link) => ( + + {link.text} + + ))} + +
+
+
+ ); +}; + +export default LayoutFooter; diff --git a/packages/web/src/components/Layout/index.jsx b/packages/web/src/components/Layout/index.jsx index 3e96442b..e9542f9e 100644 --- a/packages/web/src/components/Layout/index.jsx +++ b/packages/web/src/components/Layout/index.jsx @@ -19,6 +19,8 @@ import AppBar from 'components/AppBar'; import Drawer from 'components/Drawer'; import useAutomatischConfig from 'hooks/useAutomatischConfig'; +import Footer from './Footer'; + const additionalDrawerLinkIcons = { Security: SecurityIcon, ArrowBackIosNew: ArrowBackIosNewIcon, @@ -141,6 +143,7 @@ function PublicLayout({ children }) { {children} +