diff --git a/apps/frontend/src/components/HealthConsentModal.css b/apps/frontend/src/components/HealthConsentModal.css new file mode 100644 index 0000000..3c76868 --- /dev/null +++ b/apps/frontend/src/components/HealthConsentModal.css @@ -0,0 +1,8 @@ +.health-consent-modal { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); padding: 2rem 2.25rem 1.75rem; width: 100%; max-width: 360px; box-shadow: 0 20px 60px rgba(28, 25, 23, 0.18); animation: slideUp 0.18s ease; } +.health-consent-title { font-size: 1.15rem; font-weight: 700; color: var(--text-main); margin: 0 0 0.75rem; } +.health-consent-desc { font-size: 0.9rem; color: var(--text-muted); line-height: 1.6; margin: 0 0 1.5rem; } +.health-consent-actions { display: flex; flex-direction: column; gap: 0.5rem; } +.health-consent-btn { width: 100%; padding: 0.65rem 1rem; border-radius: 999px; border: none; font-size: 0.9rem; font-weight: 600; cursor: pointer; transition: opacity 0.15s; } +.health-consent-btn:hover { opacity: 0.85; } +.health-consent-btn--accept { background: var(--primary); color: var(--on-primary); } +.health-consent-btn--cancel { background: var(--surface-muted); color: var(--text-main); border: 1px solid var(--border); } diff --git a/apps/frontend/src/components/HealthConsentModal.jsx b/apps/frontend/src/components/HealthConsentModal.jsx new file mode 100644 index 0000000..24da0db --- /dev/null +++ b/apps/frontend/src/components/HealthConsentModal.jsx @@ -0,0 +1,28 @@ +import React, { useEffect } from 'react'; +import { useTranslation } from '../i18n'; +import './HealthConsentModal.css'; + +function HealthConsentModal({ onAccept, onCancel }) { + const { t } = useTranslation(); + + useEffect(() => { + function handleKey(e) { if (e.key === 'Escape') onCancel(); } + document.addEventListener('keydown', handleKey); + return () => document.removeEventListener('keydown', handleKey); + }, [onCancel]); + + return ( +
+
e.stopPropagation()} role="dialog" aria-modal="true" aria-label={t('health_consent.title')}> +

{t('health_consent.title')}

+

{t('health_consent.description')}

+
+ + +
+
+
+ ); +} + +export default HealthConsentModal; diff --git a/apps/frontend/src/views/PrivacyView.css b/apps/frontend/src/views/PrivacyView.css new file mode 100644 index 0000000..cf32582 --- /dev/null +++ b/apps/frontend/src/views/PrivacyView.css @@ -0,0 +1,14 @@ +.privacy-view { width: 100%; max-width: 640px; margin: 0 auto; padding: 1rem 1.25rem 2rem; animation: fadeInUp 0.3s ease-out; } +.privacy-header { display: flex; align-items: center; gap: 0.75rem; margin-bottom: 1.5rem; } +.privacy-back { background: var(--surface-muted); border: 1px solid var(--border); border-radius: var(--radius); width: 36px; height: 36px; display: flex; align-items: center; justify-content: center; cursor: pointer; font-size: 1.1rem; color: var(--text-main); flex-shrink: 0; transition: background 0.15s; } +.privacy-back:hover { background: var(--border); } +.privacy-title { font-size: 1.3rem; font-weight: 700; color: var(--text-main); margin: 0; } +.privacy-updated { font-size: 0.8rem; color: var(--text-muted); margin: 0 0 1.5rem; } +.privacy-content { display: flex; flex-direction: column; gap: 1.5rem; } +.privacy-section { background: var(--surface-muted); border-radius: var(--radius); padding: 1.25rem; } +.privacy-section-title { font-size: 1rem; font-weight: 700; color: var(--text-main); margin: 0 0 0.75rem; } +.privacy-section-content { font-size: 0.88rem; color: var(--text-muted); line-height: 1.6; } +.privacy-section-content p { margin: 0 0 0.5rem; } +.privacy-section-content p:last-child { margin-bottom: 0; } +@keyframes fadeInUp { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: translateY(0); } } +@media (max-width: 768px) { .privacy-view { padding: 0.75rem 1rem 2rem; } } diff --git a/apps/frontend/src/views/PrivacyView.jsx b/apps/frontend/src/views/PrivacyView.jsx new file mode 100644 index 0000000..8ee184e --- /dev/null +++ b/apps/frontend/src/views/PrivacyView.jsx @@ -0,0 +1,32 @@ +import React from 'react'; +import { useTranslation } from '../i18n'; +import './PrivacyView.css'; + +function PrivacyView({ onBack }) { + const { t } = useTranslation(); + const sections = ['controller', 'data_collected', 'purpose', 'legal_basis', 'external_services', 'retention', 'rights', 'contact', 'cookies']; + + return ( +
+
+ +

{t('privacy.title')}

+
+
+

{t('privacy.last_updated')}

+ {sections.map(section => ( +
+

{t(`privacy.section.${section}.title`)}

+
+ {t(`privacy.section.${section}.content`).split('\n').map((line, i) => ( +

{line}

+ ))} +
+
+ ))} +
+
+ ); +} + +export default PrivacyView;