diff --git a/pcweb/pages/__init__.py b/pcweb/pages/__init__.py index 9dd147ddd1..db1cb0d5b9 100644 --- a/pcweb/pages/__init__.py +++ b/pcweb/pages/__init__.py @@ -1,9 +1,11 @@ from pcweb.route import Route from .affiliates import affiliates as affiliates +from .security import security as security from .databricks.databricks import databricks_page as databricks_page from .use_cases.use_cases import use_cases_page as use_cases_page from .blog import blog_routes from .customers.data.customers import customers_routes +from .security.security import security as security from .customers.landing import customers as customers from .docs import doc_routes from .errors import errors as errors diff --git a/pcweb/pages/security/data.py b/pcweb/pages/security/data.py new file mode 100644 index 0000000000..6b54ca4efb --- /dev/null +++ b/pcweb/pages/security/data.py @@ -0,0 +1,72 @@ +"""Security page data configuration.""" + +# Feature data organized by category +SECURITY_FEATURES = { + "Data Protection": [ + ("Data Encryption", "AES-256 encryption at rest, TLS 1.2+ in transit."), + ("Database Backups", "Daily encrypted backups with 30-day retention."), + ("Data Segregation", "Customer data is logically isolated per tenant."), + ], + "Product Security": [ + ("Penetration Testing", "External tests conducted annually."), + ("Secure Development Lifecycle", "Code reviews, linting, and security scans."), + ("Dependency Management", "Automated scanning for vulnerabilities."), + ], + "Enterprise Security": [ + ("SSO/SAML", "Supports major identity providers for centralized auth."), + ("Granular Permissions", "Role-based access control across teams."), + ("Audit Logs", "Track every access and change in the system."), + ], + "Data Privacy": [ + ("GDPR & CCPA Ready", "Compliant data handling and user rights."), + ("Data Deletion Requests", "Users can request full data erasure."), + ("Privacy by Design", "Privacy baked into product architecture."), + ], +} + +# Trust services criteria for the grid cards +TRUST_SERVICES_CRITERIA = [ + { + "title": "Security", + "description": "Protection of systems and data from unauthorized access through firewalls, multi-factor authentication, and continuous monitoring.", + "icon": "shield", + }, + { + "title": "Availability", + "description": "Ensures that systems are operational and accessible as promised, with redundancy, failover systems, and uptime monitoring in place.", + "icon": "globe", + }, + { + "title": "Confidentiality", + "description": "Restricts access to sensitive information using encryption, role-based access controls, and secure data handling policies.", + "icon": "backend_auth", + }, + { + "title": "Processing Integrity", + "description": "Guarantees that system operations are accurate, timely, and authorized, using code reviews, automated tests, and deployment controls.", + "icon": "code_custom", + }, + { + "title": "Privacy", + "description": "Covers the collection, use, retention, and disposal of personal information according to regulatory and contractual obligations.", + "icon": "clipboard", + } +] + +# Page content configuration +PAGE_CONTENT = { + "title": "Security, Compliance, and Trust at Reflex", + "subtitle": "We're committed to protecting your data through enterprise-grade security practices and full SOC 2 compliance.", + "showcase": { + "title": "Secure by default", + "subtitle": "SOC 2 compliant with enterprise-grade security and flexible deployment options.", + "logos": [ + {"src": "/soc2.webp", "alt": "SOC 2 Compliance"}, + {"src": "/databricks-partner.svg", "alt": "Databricks Partner"} + ] + }, + "table": { + "title": "Enterprise-Grade Security at Every Layer", + "description": "From data protection to privacy compliance, Reflex is built with security-first principles to meet the needs of modern teams and enterprises." + } +} diff --git a/pcweb/pages/security/security.py b/pcweb/pages/security/security.py new file mode 100644 index 0000000000..fa95764161 --- /dev/null +++ b/pcweb/pages/security/security.py @@ -0,0 +1,19 @@ +"""Main security page implementation.""" + +import reflex as rx +from pcweb.templates.mainpage import mainpage +from .views import security_title, security_grid, features_table_section + + +@mainpage(path="/security", title="Security - Reflex") +def security(): + """Main security page with modular sections.""" + return rx.box( + rx.box( + security_title(), + security_grid(), + features_table_section(), + class_name="flex flex-col relative justify-center items-center w-full", + ), + class_name="flex flex-col w-full", + ) diff --git a/pcweb/pages/security/views/__init__.py b/pcweb/pages/security/views/__init__.py new file mode 100644 index 0000000000..6517bdd5d1 --- /dev/null +++ b/pcweb/pages/security/views/__init__.py @@ -0,0 +1,11 @@ +"""Views package initialization.""" + +from .header import security_title +from .grid import security_grid +from .features_table import features_table_section + +__all__ = [ + "security_title", + "security_grid", + "features_table_section" +] diff --git a/pcweb/pages/security/views/features_table.py b/pcweb/pages/security/views/features_table.py new file mode 100644 index 0000000000..f2b147f075 --- /dev/null +++ b/pcweb/pages/security/views/features_table.py @@ -0,0 +1,67 @@ +"""Features table section for security page.""" + +import reflex as rx +from pcweb.pages.pricing.table import ( + create_feature_table_header, + create_feature_row, + create_table_body, + TABLE_STYLE +) +from ..data import SECURITY_FEATURES, PAGE_CONTENT + + +def security_table_header() -> rx.Component: + """Header section for the security features table.""" + table_content = PAGE_CONTENT["table"] + + return rx.box( + rx.el.h3( + table_content["title"], + class_name="text-slate-12 text-3xl font-semibold text-center", + ), + rx.el.p( + table_content["description"], + class_name="text-slate-9 text-xl font-medium text-center mt-4", + ), + class_name="flex items-center justify-between text-slate-11 flex-col py-[5rem] max-w-[64.19rem] mx-auto w-full px-6 lg:border-x border-slate-3", + ) + + +def create_security_table_section(category: str, features: list) -> list: + """Create a table section with header and body for a security category.""" + return [ + rx.table.header( + create_feature_table_header(category), + class_name="relative", + ), + create_table_body( + *[ + create_feature_row(feature, description) + for feature, description in features + ], + ), + ] + + +def security_features_table() -> rx.Component: + """Complete security features table with all categories.""" + table_sections = [] + + # Generate all table sections dynamically + for category, features in SECURITY_FEATURES.items(): + table_sections.extend(create_security_table_section(category, features)) + + return rx.table.root( + rx.el.style(TABLE_STYLE), + *table_sections, + class_name="w-full overflow-x-auto max-w-[69.125rem] -mt-[2rem]", + ) + + +def features_table_section() -> rx.Component: + """Complete features table section with header and table.""" + return rx.box( + security_table_header(), + security_features_table(), + class_name="flex-col w-full max-w-[69.125rem] desktop-only", + ) diff --git a/pcweb/pages/security/views/grid.py b/pcweb/pages/security/views/grid.py new file mode 100644 index 0000000000..63dec3549f --- /dev/null +++ b/pcweb/pages/security/views/grid.py @@ -0,0 +1,117 @@ +"""Grid section for security page featuring trust services criteria.""" + +import reflex as rx +from pcweb.components.icons import get_icon +from ..data import PAGE_CONTENT, TRUST_SERVICES_CRITERIA + + +def security_card( + title: str, + description: str, + icon: str, + cols: str = "1", + class_name: str = "", +) -> rx.Component: + """Individual security feature card component.""" + return rx.box( + rx.box( + _card_header(title, icon), + _card_description(description), + class_name="flex flex-col gap-[0.875rem]", + ), + class_name=f"overflow-hidden p-8 w-full {class_name} lg:col-span-{cols} h-[13rem] lg:h-[11rem] border-slate-3", + ) + + +def _card_header(title: str, icon: str) -> rx.Component: + """Card header with icon and title.""" + return rx.box( + get_icon(icon, class_name="!text-slate-9"), + rx.el.h3(title, class_name="text-slate-12 text-base font-semibold"), + class_name="flex flex-row items-center gap-2", + ) + + +def _card_description(description: str) -> rx.Component: + """Card description text.""" + return rx.el.p( + description, class_name="text-slate-9 font-medium text-sm text-start" + ) + + +def outcomes_showcase() -> rx.Component: + """Central outcomes showcase component with prominent display.""" + showcase = PAGE_CONTENT["showcase"] + + return rx.box( + rx.box( + rx.box( + rx.el.h2( + showcase["title"], + class_name="text-slate-12 text-lg lg:text-2xl font-semibold text-center", + ), + rx.el.h3( + showcase["subtitle"], + class_name="text-slate-9 text-md lg:text-xl font-semibold text-center", + ), + class_name="flex flex-col gap-2 p-10 items-center justify-center", + ), + rx.box( + rx.box( + *[ + rx.image( + src=logo["src"], + alt=logo["alt"], + class_name="h-24 w-auto" + ) + for logo in showcase["logos"] + ], + class_name="flex flex-row gap-10 items-center justify-center", + ), + class_name="p-10 flex items-center justify-center", + ), + class_name="flex flex-col justify-center items-center h-full", + ), + class_name="h-full w-full flex flex-col justify-center items-center relative overflow-hidden lg:row-span-3 lg:col-span-1 lg:border-l lg:border-r border-slate-3 p-8 lg:p-1", + ) + + +def security_grid() -> rx.Component: + """Main security features grid component with responsive layout.""" + criteria = TRUST_SERVICES_CRITERIA + + # Mobile layout - simple single column stack + mobile_layout = rx.box( + *[security_card(**criterion) for criterion in criteria], + class_name="lg:hidden flex flex-col divide-y divide-slate-3 border border-slate-3" + ) + + # Desktop layout - complex grid with showcase + desktop_layout = rx.box( + # Last card (spans 2 columns) -> moved to top + security_card( + **criteria[4], + cols="2", + class_name="lg:col-span-2" + ), + + # Center showcase (spans 3 rows, 1 column) + outcomes_showcase(), + + # First 2 cards + *[security_card(**criterion) for criterion in criteria[:2]], + + # Next 2 cards + *[security_card(**criterion) for criterion in criteria[2:4]], + + class_name=( + "hidden lg:grid lg:grid-cols-3 lg:grid-rows-3 " + "w-full border border-slate-3" + ) + ) + + return rx.box( + mobile_layout, + desktop_layout, + class_name="flex flex-row max-w-[64.19rem] justify-center w-full", + ) diff --git a/pcweb/pages/security/views/header.py b/pcweb/pages/security/views/header.py new file mode 100644 index 0000000000..9b5cd4a8d8 --- /dev/null +++ b/pcweb/pages/security/views/header.py @@ -0,0 +1,27 @@ +"""Header section for security page.""" + +import reflex as rx +from pcweb.components.hosting_banner import HostingBannerState +from ..data import PAGE_CONTENT + + +def security_title() -> rx.Component: + """Main title section for security page.""" + content = PAGE_CONTENT + + return rx.el.section( + rx.el.h1( + content["title"], + class_name="max-w-full inline-block bg-clip-text bg-gradient-to-r from-slate-12 to-slate-11 w-full text-4xl lg:text-5xl text-center text-transparent text-balance mx-auto break-words font-semibold", + ), + rx.el.h2( + content["subtitle"], + class_name="max-w-full w-full font-large text-center text-slate-11 -mt-2 font-normal text-[1.25rem] mx-auto text-balance word-wrap break-words md:whitespace-pre", + ), + class_name="flex flex-col justify-center items-center gap-4 mx-auto w-full max-w-[64.19rem] lg:border-x border-slate-3 pb-4 lg:pb-[7.875rem]" + + rx.cond( + HostingBannerState.show_banner, + " lg:pt-[15.2rem] pt-[8rem]", + " lg:pt-[13.2rem] pt-[6rem]", + ), + )