diff --git a/app/common/src/main/java/stirling/software/common/util/RequestUriUtils.java b/app/common/src/main/java/stirling/software/common/util/RequestUriUtils.java index 0dfdfa69e..2dd3d420a 100644 --- a/app/common/src/main/java/stirling/software/common/util/RequestUriUtils.java +++ b/app/common/src/main/java/stirling/software/common/util/RequestUriUtils.java @@ -159,6 +159,8 @@ public class RequestUriUtils { || trimmedUri.startsWith( "/api/v1/proprietary/ui-data/login") // Login page config (SSO providers + // enableLogin) + || trimmedUri.startsWith( + "/api/v1/ui-data/footer-info") // Public footer configuration || trimmedUri.startsWith("/v1/api-docs") || trimmedUri.startsWith("/api/v1/invite/validate") || trimmedUri.startsWith("/api/v1/invite/accept") diff --git a/app/core/src/main/java/stirling/software/SPDF/controller/api/UIDataController.java b/app/core/src/main/java/stirling/software/SPDF/controller/api/UIDataController.java index ad2fead01..18445d7da 100644 --- a/app/core/src/main/java/stirling/software/SPDF/controller/api/UIDataController.java +++ b/app/core/src/main/java/stirling/software/SPDF/controller/api/UIDataController.java @@ -58,6 +58,21 @@ public class UIDataController { this.runtimePathConfig = runtimePathConfig; } + @GetMapping("/footer-info") + @Operation(summary = "Get public footer configuration data") + public ResponseEntity getFooterData() { + FooterData data = new FooterData(); + data.setAnalyticsEnabled(applicationProperties.getSystem().getEnableAnalytics()); + data.setTermsAndConditions(applicationProperties.getLegal().getTermsAndConditions()); + data.setPrivacyPolicy(applicationProperties.getLegal().getPrivacyPolicy()); + data.setAccessibilityStatement( + applicationProperties.getLegal().getAccessibilityStatement()); + data.setCookiePolicy(applicationProperties.getLegal().getCookiePolicy()); + data.setImpressum(applicationProperties.getLegal().getImpressum()); + + return ResponseEntity.ok(data); + } + @GetMapping("/home") @Operation(summary = "Get home page data") public ResponseEntity getHomeData() { @@ -237,6 +252,16 @@ public class UIDataController { } // Data classes + @Data + public static class FooterData { + private Boolean analyticsEnabled; + private String termsAndConditions; + private String privacyPolicy; + private String accessibilityStatement; + private String cookiePolicy; + private String impressum; + } + @Data public static class HomeData { private boolean showSurveyFromDocker; diff --git a/app/core/src/main/resources/static/css/cookieconsentCustomisation.css b/app/core/src/main/resources/static/css/cookieconsentCustomisation.css index bfd0e87f1..86ff70bfb 100644 --- a/app/core/src/main/resources/static/css/cookieconsentCustomisation.css +++ b/app/core/src/main/resources/static/css/cookieconsentCustomisation.css @@ -24,8 +24,8 @@ --cc-toggle-on-knob-bg: var(--cc-btn-primary-color); --cc-toggle-off-knob-bg: var(--cc-btn-primary-color); - --cc-toggle-enabled-icon-color: var(--cc-btn-primary-color); - --cc-toggle-disabled-icon-color: var(--cc-btn-primary-color); + --cc-toggle-enabled-icon-color: var(--cc-btn-primary-color); + --cc-toggle-disabled-icon-color: var(--cc-btn-primary-color); --cc-toggle-readonly-bg: var(--md-sys-color-surface); --cc-toggle-readonly-knob-bg: var(--md-sys-color-outline); @@ -34,10 +34,10 @@ --cc-section-category-border: var(--md-sys-color-outline); --cc-cookie-category-block-bg: var(--cc-btn-secondary-bg); - --cc-cookie-category-block-border: var(--cc-btn-secondary-bg); + --cc-cookie-category-block-border: var(--cc-btn-secondary-bg); --cc-cookie-category-block-hover-bg: var(--cc-btn-secondary-bg); --cc-cookie-category-block-hover-border: var(--cc-btn-secondary-bg); - + --cc-cookie-category-expanded-block-bg: var(--cc-btn-secondary-bg); --cc-cookie-category-expanded-block-hover-bg: var(--cc-toggle-readonly-bg); @@ -47,7 +47,7 @@ */ --cc-footer-bg: var(--cc-bg); --cc-footer-color: var(--cc-primary-color); - --cc-footer-border-color: var(--cc-bg); + --cc-footer-border-color: var(--cc-bg); } .cm__body{ max-width: 90% !important; @@ -81,4 +81,4 @@ /* Lower z-index so cookie banner appears behind onboarding modals */ #cc-main { z-index: 100 !important; -} \ No newline at end of file +} diff --git a/app/proprietary/src/main/java/stirling/software/proprietary/security/filter/UserAuthenticationFilter.java b/app/proprietary/src/main/java/stirling/software/proprietary/security/filter/UserAuthenticationFilter.java index 896801ce8..fe57b3997 100644 --- a/app/proprietary/src/main/java/stirling/software/proprietary/security/filter/UserAuthenticationFilter.java +++ b/app/proprietary/src/main/java/stirling/software/proprietary/security/filter/UserAuthenticationFilter.java @@ -299,7 +299,8 @@ public class UserAuthenticationFilter extends OncePerRequestFilter { contextPath + "/api/v1/auth/refresh", contextPath + "/api/v1/auth/me", contextPath + "/api/v1/invite/validate", - contextPath + "/api/v1/invite/accept" + contextPath + "/api/v1/invite/accept", + contextPath + "/api/v1/ui-data/footer-info" }; for (String pattern : publicApiPatterns) { diff --git a/frontend/public/css/cookieconsentCustomisation.css b/frontend/public/css/cookieconsentCustomisation.css index 2c02d7407..ec360c20b 100644 --- a/frontend/public/css/cookieconsentCustomisation.css +++ b/frontend/public/css/cookieconsentCustomisation.css @@ -54,17 +54,17 @@ --cc-secondary-color: #b0b0b0; --cc-btn-primary-bg: #4dabf7; - --cc-btn-primary-color: #2d2d2d; + --cc-btn-primary-color: #ffffff; --cc-btn-primary-border-color: #4dabf7; --cc-btn-primary-hover-bg: #3d3d3d; - --cc-btn-primary-hover-color: #e5e5e5; + --cc-btn-primary-hover-color: #ffffff; --cc-btn-primary-hover-border-color: #3d3d3d; --cc-btn-secondary-bg: #3d3d3d; - --cc-btn-secondary-color: #e5e5e5; + --cc-btn-secondary-color: #ffffff; --cc-btn-secondary-border-color: #3d3d3d; --cc-btn-secondary-hover-bg: #4dabf7; - --cc-btn-secondary-hover-color: #2d2d2d; + --cc-btn-secondary-hover-color: #ffffff; --cc-btn-secondary-hover-border-color: #4dabf7; --cc-separator-border-color: #555555; @@ -180,4 +180,27 @@ /* Lower z-index so cookie banner appears behind onboarding modals */ #cc-main { z-index: 100 !important; +} + +/* Ensure consent modal text is visible in both themes */ +#cc-main .cm { + background: var(--cc-bg) !important; + color: var(--cc-primary-color) !important; +} + +#cc-main .cm__title { + color: var(--cc-primary-color) !important; +} + +#cc-main .cm__desc { + color: var(--cc-primary-color) !important; +} + +#cc-main .cm__footer { + color: var(--cc-primary-color) !important; +} + +#cc-main .cm__footer-links a, +#cc-main .cm__link { + color: var(--cc-primary-color) !important; } \ No newline at end of file diff --git a/frontend/src/core/components/shared/Footer.tsx b/frontend/src/core/components/shared/Footer.tsx index 52ee4165b..172861d1b 100644 --- a/frontend/src/core/components/shared/Footer.tsx +++ b/frontend/src/core/components/shared/Footer.tsx @@ -1,6 +1,7 @@ import { Flex } from '@mantine/core'; import { useTranslation } from 'react-i18next'; import { useCookieConsent } from '@app/hooks/useCookieConsent'; +import { useFooterInfo } from '@app/hooks/useFooterInfo'; interface FooterProps { privacyPolicy?: string; @@ -9,6 +10,7 @@ interface FooterProps { cookiePolicy?: string; impressum?: string; analyticsEnabled?: boolean; + forceLightMode?: boolean; } export default function Footer({ @@ -17,10 +19,36 @@ export default function Footer({ accessibilityStatement, cookiePolicy, impressum, - analyticsEnabled = false + analyticsEnabled, + forceLightMode = false }: FooterProps) { const { t } = useTranslation(); - const { showCookiePreferences } = useCookieConsent({ analyticsEnabled }); + const { footerInfo } = useFooterInfo(); + + console.log('[Footer] Props analyticsEnabled:', analyticsEnabled); + console.log('[Footer] Fetched footerInfo:', footerInfo); + + // Use props if provided, otherwise fall back to fetched footer info + const finalAnalyticsEnabled = analyticsEnabled ?? footerInfo?.analyticsEnabled ?? false; + const finalPrivacyPolicy = privacyPolicy ?? footerInfo?.privacyPolicy; + const finalTermsAndConditions = termsAndConditions ?? footerInfo?.termsAndConditions; + const finalAccessibilityStatement = accessibilityStatement ?? footerInfo?.accessibilityStatement; + const finalCookiePolicy = cookiePolicy ?? footerInfo?.cookiePolicy; + const finalImpressum = impressum ?? footerInfo?.impressum; + + console.log('[Footer] Final analyticsEnabled:', finalAnalyticsEnabled); + + const { showCookiePreferences } = useCookieConsent({ analyticsEnabled: finalAnalyticsEnabled, forceLightMode }); + + // Default URLs + const defaultTermsUrl = "https://www.stirling.com/legal/terms-of-service"; + const defaultPrivacyUrl = "https://www.stirling.com/legal/privacy-policy"; + const defaultAccessibilityUrl = "https://www.stirling.com/accessibility"; + + // Use provided URLs or fall back to defaults + const finalTermsUrl = finalTermsAndConditions || defaultTermsUrl; + const finalPrivacyUrl = finalPrivacyPolicy || defaultPrivacyUrl; + const finalAccessibilityUrl = finalAccessibilityStatement || defaultAccessibilityUrl; // Helper to check if a value is valid (not null/undefined/empty string) const isValidLink = (link?: string) => link && link.trim().length > 0; @@ -28,8 +56,8 @@ export default function Footer({ return (
+ style={{ + fontSize: '0.75rem', + color: forceLightMode ? '#495057' : undefined + }}> {t('survey.nav', 'Survey')} - {isValidLink(privacyPolicy) && ( + + {t('legal.privacy', 'Privacy Policy')} + + + {t('legal.terms', 'Terms and Conditions')} + + + {t('legal.accessibility', 'Accessibility')} + + {isValidLink(finalCookiePolicy) && ( - {t('legal.privacy', 'Privacy Policy')} - - )} - {isValidLink(termsAndConditions) && ( - - {t('legal.terms', 'Terms and Conditions')} - - )} - {isValidLink(accessibilityStatement) && ( - - {t('legal.accessibility', 'Accessibility')} - - )} - {isValidLink(cookiePolicy) && ( - {t('legal.cookie', 'Cookie Policy')} )} - {isValidLink(impressum) && ( + {isValidLink(finalImpressum) && ( {t('legal.impressum', 'Impressum')} )} - {analyticsEnabled && ( + {finalAnalyticsEnabled && (
+
+
); }