feat: add legal page with privacy policy and transparency info#3825
feat: add legal page with privacy policy and transparency info#3825MarkusNeusinger merged 5 commits intomainfrom
Conversation
- Implement LegalPage component with legal notice and privacy policy - Add link to legal page in footer - Update router to include legal route
- Implement bot-optimized legal page with appropriate og:tags - Add legal page URL to sitemap - Update documentation to include legal page in routes
- Adjust font sizes for headings and subheadings - Modify breadcrumb margin for better spacing - Add links for Legal Notice, Privacy Policy, and Transparency sections - Enhance disclaimer and analytics descriptions - Update monthly costs section with accurate figures - Improve overall layout and styling for better readability
There was a problem hiding this comment.
Pull request overview
This pull request adds a comprehensive legal page to the website containing legal notice, privacy policy, and transparency information. The implementation includes proper SEO support, navigation integration, and consistent styling with the rest of the application.
Changes:
- New
/legalroute with detailed legal information (operator details, privacy policy, transparency about tech stack and costs) - Footer link to access the legal page
- SEO enhancements including sitemap entry and bot-optimized page with Open Graph tags
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| app/src/pages/LegalPage.tsx | New legal page component with three sections: Legal Notice, Privacy Policy, and Transparency |
| app/src/router.tsx | Added route configuration for /legal path |
| app/src/components/Footer.tsx | Added footer link to legal page with RouterLink |
| app/src/hooks/useUrlSync.ts | Removed unused FilterCategory import (cleanup) |
| api/routers/seo.py | Added /seo-proxy/legal endpoint and legal page URL to sitemap |
| docs/reference/seo.md | Updated documentation to reflect legal page in SEO proxy endpoints and sitemap |
| docs/reference/plausible.md | Added legal page to tracked pages documentation |
| <Link href="https://postgresql.org" target="_blank" rel="noopener" sx={{ color: '#3776AB' }}> | ||
| PostgreSQL | ||
| </Link>{' '} | ||
| 18 |
There was a problem hiding this comment.
The PostgreSQL version listed as "18" appears to be incorrect. According to the project documentation in .github/copilot-instructions.md, the project uses PostgreSQL 15. Please update this to match the actual deployed version.
| 18 | |
| 15 |
| @router.get("/seo-proxy/legal") | ||
| async def seo_legal(): | ||
| """Bot-optimized legal page with correct og:tags.""" | ||
| return HTMLResponse( | ||
| BOT_HTML_TEMPLATE.format( | ||
| title="Legal | pyplots.ai", | ||
| description="Legal notice, privacy policy, and transparency information for pyplots.ai", | ||
| image=DEFAULT_HOME_IMAGE, | ||
| url="https://pyplots.ai/legal", | ||
| ) | ||
| ) |
There was a problem hiding this comment.
The new seo_legal endpoint lacks test coverage. Based on the existing test patterns in tests/unit/api/test_routers.py (TestSeoProxyRouter class), this endpoint should have a unit test to verify it returns the correct HTML response with proper og:tags for the legal page.
| '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">', | ||
| " <url><loc>https://pyplots.ai/</loc></url>", | ||
| " <url><loc>https://pyplots.ai/catalog</loc></url>", | ||
| " <url><loc>https://pyplots.ai/legal</loc></url>", |
There was a problem hiding this comment.
The addition of the legal page URL to the sitemap lacks test coverage. The existing tests in tests/unit/api/test_routers.py (test_sitemap_structure and test_sitemap_with_db methods) should be updated to verify that the sitemap includes the legal page URL.
| <Link | ||
| component={RouterLink} | ||
| to="/legal" | ||
| sx={{ | ||
| color: '#9ca3af', | ||
| textDecoration: 'none', | ||
| '&:hover': { color: '#6b7280' }, | ||
| }} | ||
| > | ||
| legal | ||
| </Link> |
There was a problem hiding this comment.
The new legal link in the footer is missing an onClick handler for analytics tracking. The other footer links (linkedin, github, stats) all have onClick handlers that call onTrackEvent. For consistency and to track user navigation to the legal page from the footer, consider adding an onClick handler similar to the pattern used for external links.
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
- Clarified rights under GDPR and Swiss DSG - Improved wording for better readability
- Adjust legal page component in footer to include tracking event - Update cloud SQL version in copilot instructions
| <Link href="https://plausible.io" target="_blank" rel="noopener" sx={{ color: '#3776AB' }}> | ||
| Plausible Analytics | ||
| </Link> | ||
| , a privacy-focused analytics tool. It collects no personal data, uses no cookies, and does not track you | ||
| across websites. We track: page views, navigation patterns, code copies, image downloads, search queries, | ||
| filter usage, and UI interactions. When you share a link, we detect which platform requests the preview | ||
| (e.g., LinkedIn, WhatsApp). All data is aggregated and anonymous. | ||
| </Typography> | ||
| <Typography sx={textStyle}> | ||
| <strong>Public Dashboard</strong>: Our analytics are{' '} | ||
| <Link href="https://plausible.io/pyplots.ai" target="_blank" rel="noopener" sx={{ color: '#3776AB' }}> | ||
| fully public | ||
| </Link>{' '} | ||
| – see exactly what we see. | ||
| </Typography> | ||
| <Typography sx={textStyle}> | ||
| <strong>Server Logs</strong>: Technical server logs including IP addresses, request URLs, and user agents | ||
| are retained for 30 days via{' '} | ||
| <Link href="https://cloud.google.com/logging" target="_blank" rel="noopener" sx={{ color: '#3776AB' }}> | ||
| Google Cloud Logging | ||
| </Link>{' '} | ||
| for security and debugging purposes. | ||
| </Typography> | ||
|
|
||
| <Typography sx={subheadingStyle}>What We Do NOT Collect</Typography> | ||
| <Typography sx={textStyle}> | ||
| • No user accounts or personal profiles | ||
| <br /> | ||
| • No personal data (names, emails, etc.) | ||
| <br /> | ||
| • No cookies at all (we use localStorage for UI preferences only) | ||
| <br />• <strong>No AI training</strong>: Your interactions are not used to train AI models | ||
| </Typography> | ||
|
|
||
| <Typography sx={textStyle}> | ||
| <strong>Contributors</strong>: If you suggest a plot type via GitHub, your GitHub username may be credited | ||
| in the specification metadata. This is public information from your GitHub profile. | ||
| </Typography> | ||
|
|
||
| <Typography sx={subheadingStyle}>Hosting & Third Parties</Typography> | ||
| <Typography sx={textStyle}>All services are hosted in the EU (Netherlands, europe-west4):</Typography> | ||
| <Table sx={tableStyle}> | ||
| <TableBody> | ||
| <TableRow> | ||
| <TableCell>Hosting</TableCell> | ||
| <TableCell>Google Cloud Run (Netherlands)</TableCell> | ||
| </TableRow> | ||
| <TableRow> | ||
| <TableCell>Database</TableCell> | ||
| <TableCell>Google Cloud SQL (Netherlands)</TableCell> | ||
| </TableRow> | ||
| <TableRow> | ||
| <TableCell>Storage</TableCell> | ||
| <TableCell>Google Cloud Storage (Netherlands)</TableCell> | ||
| </TableRow> | ||
| <TableRow> | ||
| <TableCell>Analytics</TableCell> | ||
| <TableCell>Plausible Analytics (EU, proxied)</TableCell> | ||
| </TableRow> | ||
| </TableBody> | ||
| </Table> | ||
|
|
||
| <Typography sx={subheadingStyle}>Your Rights</Typography> | ||
| <Typography sx={textStyle}> | ||
| You have the right to access, rectify, erase, and export your data. Since we do not store personal data, | ||
| there is typically nothing to delete or export. For questions, contact{' '} | ||
| <Link href="mailto:admin@pyplots.ai" sx={{ color: '#3776AB' }}> | ||
| admin@pyplots.ai | ||
| </Link> | ||
| . | ||
| </Typography> | ||
| </Paper> | ||
|
|
||
| {/* Transparency */} | ||
| <Paper component="section" id="transparency" sx={{ p: 3, mb: 2 }}> | ||
| <Typography variant="h2" sx={headingStyle}> | ||
| Transparency | ||
| </Typography> | ||
|
|
||
| <Typography sx={textStyle}> | ||
| This project is open source and committed to full transparency about how it works and what it costs. | ||
| </Typography> | ||
|
|
||
| <Typography sx={subheadingStyle}>Technology Stack</Typography> | ||
| <Table sx={tableStyle}> | ||
| <TableBody> | ||
| <TableRow> | ||
| <TableCell>Editor</TableCell> | ||
| <TableCell> | ||
| <Link href="https://www.jetbrains.com/pycharm/" target="_blank" rel="noopener" sx={{ color: '#3776AB' }}> | ||
| JetBrains PyCharm | ||
| </Link> | ||
| </TableCell> | ||
| </TableRow> | ||
| <TableRow> | ||
| <TableCell>Frontend</TableCell> | ||
| <TableCell> | ||
| <Link href="https://react.dev" target="_blank" rel="noopener" sx={{ color: '#3776AB' }}> | ||
| React | ||
| </Link>{' '} | ||
| 19,{' '} | ||
| <Link href="https://vite.dev" target="_blank" rel="noopener" sx={{ color: '#3776AB' }}> | ||
| Vite | ||
| </Link> | ||
| ,{' '} | ||
| <Link href="https://mui.com" target="_blank" rel="noopener" sx={{ color: '#3776AB' }}> | ||
| MUI | ||
| </Link>{' '} | ||
| 7,{' '} | ||
| <Link href="https://typescriptlang.org" target="_blank" rel="noopener" sx={{ color: '#3776AB' }}> | ||
| TypeScript | ||
| </Link> | ||
| </TableCell> | ||
| </TableRow> | ||
| <TableRow> | ||
| <TableCell>Backend</TableCell> | ||
| <TableCell> | ||
| <Link href="https://python.org" target="_blank" rel="noopener" sx={{ color: '#3776AB' }}> | ||
| Python | ||
| </Link>{' '} | ||
| 3.13,{' '} | ||
| <Link href="https://fastapi.tiangolo.com" target="_blank" rel="noopener" sx={{ color: '#3776AB' }}> | ||
| FastAPI | ||
| </Link> | ||
| ,{' '} | ||
| <Link href="https://sqlalchemy.org" target="_blank" rel="noopener" sx={{ color: '#3776AB' }}> | ||
| SQLAlchemy | ||
| </Link> | ||
| </TableCell> | ||
| </TableRow> | ||
| <TableRow> | ||
| <TableCell>Database</TableCell> | ||
| <TableCell> | ||
| <Link href="https://postgresql.org" target="_blank" rel="noopener" sx={{ color: '#3776AB' }}> | ||
| PostgreSQL | ||
| </Link>{' '} | ||
| 18 | ||
| </TableCell> | ||
| </TableRow> | ||
| <TableRow> | ||
| <TableCell>Hosting</TableCell> | ||
| <TableCell> | ||
| <Link href="https://cloud.google.com/run" target="_blank" rel="noopener" sx={{ color: '#3776AB' }}> | ||
| Google Cloud Run | ||
| </Link>{' '} | ||
| (Netherlands) | ||
| </TableCell> | ||
| </TableRow> | ||
| <TableRow> | ||
| <TableCell>Storage</TableCell> | ||
| <TableCell> | ||
| <Link href="https://cloud.google.com/storage" target="_blank" rel="noopener" sx={{ color: '#3776AB' }}> | ||
| Google Cloud Storage | ||
| </Link> | ||
| </TableCell> | ||
| </TableRow> | ||
| <TableRow> | ||
| <TableCell>Analytics</TableCell> | ||
| <TableCell> | ||
| <Link href="https://plausible.io" target="_blank" rel="noopener" sx={{ color: '#3776AB' }}> | ||
| Plausible | ||
| </Link>{' '} | ||
| (privacy-friendly, no cookies,{' '} | ||
| <Link href="https://plausible.io/pyplots.ai" target="_blank" rel="noopener" sx={{ color: '#3776AB' }}> | ||
| public dashboard | ||
| </Link> | ||
| ) | ||
| </TableCell> | ||
| </TableRow> | ||
| <TableRow> | ||
| <TableCell>Code Generation</TableCell> | ||
| <TableCell> | ||
| <Link href="https://anthropic.com" target="_blank" rel="noopener" sx={{ color: '#3776AB' }}> | ||
| Anthropic Claude | ||
| </Link>{' '} | ||
| (code generation & review),{' '} | ||
| <Link href="https://github.com/features/copilot" target="_blank" rel="noopener" sx={{ color: '#3776AB' }}> | ||
| GitHub Copilot | ||
| </Link>{' '} | ||
| (PR reviews) | ||
| </TableCell> | ||
| </TableRow> | ||
| <TableRow> | ||
| <TableCell>Typography</TableCell> | ||
| <TableCell> | ||
| <Link href="https://www.monolisa.dev/" target="_blank" rel="noopener" sx={{ color: '#3776AB' }}> | ||
| MonoLisa | ||
| </Link>{' '} | ||
| by Marcus Sterz | ||
| </TableCell> | ||
| </TableRow> | ||
| </TableBody> | ||
| </Table> | ||
|
|
||
| <Typography sx={subheadingStyle}>Source Code</Typography> | ||
| <Typography sx={textStyle}> | ||
| The entire codebase is publicly available under the MIT License: | ||
| <br /> | ||
| <Link href={GITHUB_URL} target="_blank" rel="noopener" sx={{ color: '#3776AB' }}> | ||
| github.com/MarkusNeusinger/pyplots | ||
| </Link> |
There was a problem hiding this comment.
External links should include both noopener and noreferrer in the rel attribute for security and privacy best practices. The Footer component uses rel="noopener noreferrer" while this page uses only rel="noopener". Update all external links to use rel="noopener noreferrer" for consistency with the rest of the application.
Summary
/legalpage with Legal Notice, Privacy Policy, and Transparency sectionsLegal Notice
Privacy Policy
Transparency
UX Improvements
Test plan
🤖 Generated with Claude Code