diff --git a/package-lock.json b/package-lock.json index 253e2e73a..feee24768 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6947,6 +6947,32 @@ } } }, + "node_modules/@radix-ui/react-avatar": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-avatar/-/react-avatar-1.1.1.tgz", + "integrity": "sha512-eoOtThOmxeoizxpX6RiEsQZ2wj5r4+zoeqAwO0cBaFQGjJwIH3dIX0OCxNrCyrrdxG+vBweMETh3VziQG7c1kw==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-primitive": "2.0.0", + "@radix-ui/react-use-callback-ref": "1.1.0", + "@radix-ui/react-use-layout-effect": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-checkbox": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@radix-ui/react-checkbox/-/react-checkbox-1.1.2.tgz", @@ -33518,6 +33544,7 @@ "@heroicons/react": "^2.1.5", "@hookform/resolvers": "^3.9.0", "@radix-ui/react-accordion": "^1.2.0", + "@radix-ui/react-avatar": "^1.1.1", "@radix-ui/react-checkbox": "^1.1.1", "@radix-ui/react-collapsible": "^1.1.0", "@radix-ui/react-dialog": "^1.1.1", diff --git a/shared/locales/en/website-open-source.json b/shared/locales/en/website-open-source.json new file mode 100644 index 000000000..fa0ed51f3 --- /dev/null +++ b/shared/locales/en/website-open-source.json @@ -0,0 +1,41 @@ +{ + "metadata": { + "title": "Open-Source Insights ", + "heading": "Contributors" + }, + "contributors": { + "title": "Contributors" + }, + "hero": { + "title-1": [ + { + "text": "Tools for Change. " + }, + { + "text": "Open to All.", + "color": "accent" + } + ], + "subtitle": "Everything we build is open source—free to use, adapt, and improve. Join our volunteer community creating tools for equality and change." + }, + "overview": { + "forks": { + "title": "Forks", + "time": "last month" + }, + "commits": { + "title": "Commits", + "time": "last month" + }, + "stars": { + "title": "GitHub Stars", + "time": "last month" + } + }, + "issues": { + "title": "Open Issues", + "header": "Issue Title", + "link": "View on GitHub", + "filter": "All Issues" + } +} diff --git a/ui/package.json b/ui/package.json index 5979bc87e..39bc80fa2 100644 --- a/ui/package.json +++ b/ui/package.json @@ -24,6 +24,7 @@ "@heroicons/react": "^2.1.5", "@hookform/resolvers": "^3.9.0", "@radix-ui/react-accordion": "^1.2.0", + "@radix-ui/react-avatar": "^1.1.1", "@radix-ui/react-checkbox": "^1.1.1", "@radix-ui/react-collapsible": "^1.1.0", "@radix-ui/react-dialog": "^1.1.1", diff --git a/ui/src/components/avatar.tsx b/ui/src/components/avatar.tsx new file mode 100644 index 000000000..76f40e3cd --- /dev/null +++ b/ui/src/components/avatar.tsx @@ -0,0 +1,38 @@ +import * as AvatarPrimitive from '@radix-ui/react-avatar'; +import * as React from 'react'; + +import { cn } from '../lib/utils'; + +const Avatar = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +Avatar.displayName = AvatarPrimitive.Root.displayName; + +const AvatarImage = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +AvatarImage.displayName = AvatarPrimitive.Image.displayName; + +const AvatarFallback = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName; + +export { Avatar, AvatarFallback, AvatarImage }; diff --git a/ui/src/index.ts b/ui/src/index.ts index 7c3ff11ac..eedc35e21 100644 --- a/ui/src/index.ts +++ b/ui/src/index.ts @@ -2,6 +2,7 @@ import './globals.css'; export * from './components/accordion'; export * from './components/alert'; +export * from './components/avatar'; export * from './components/badge'; export * from './components/button'; export * from './components/card'; diff --git a/website/src/app/[lang]/[region]/(website)/open-source/(components)/contributors-client.tsx b/website/src/app/[lang]/[region]/(website)/open-source/(components)/contributors-client.tsx new file mode 100644 index 000000000..cc1db2392 --- /dev/null +++ b/website/src/app/[lang]/[region]/(website)/open-source/(components)/contributors-client.tsx @@ -0,0 +1,76 @@ +'use client'; + +import { Avatar, AvatarFallback, AvatarImage, Button, Typography } from '@socialincome/ui'; +import { useState } from 'react'; + +type ContributorProp = { + name: string; + commits: number; + avatarUrl: string; +}; + +function Contributor({ name, commits, avatarUrl }: ContributorProp) { + return ( +
+ + + {name.slice(0, 2).toUpperCase()} + +
+ + {name} + + + {commits} {commits === 1 ? 'commit' : 'commits'} + +
+
+ ); +} + +export function OpenSourceContributorsClient({ + contributors, + heading, + totalContributors, +}: { + contributors: Array<{ name: string; commits: number; avatarUrl: string; id: number }>; + heading: string; + totalContributors: number; +}) { + const [showAllContributors, setShowAllContributors] = useState(false); + + const displayedContributors = showAllContributors ? contributors : contributors.slice(0, 16); + + return ( +
+
+ + {`${totalContributors} ${heading}`} + +
+ +
+ {displayedContributors.map((contributor) => ( + + ))} +
+ + {!showAllContributors && totalContributors > 16 && ( +
+ +
+ )} +
+ ); +} diff --git a/website/src/app/[lang]/[region]/(website)/open-source/(components)/filterForm.tsx b/website/src/app/[lang]/[region]/(website)/open-source/(components)/filterForm.tsx new file mode 100644 index 000000000..d229e18bb --- /dev/null +++ b/website/src/app/[lang]/[region]/(website)/open-source/(components)/filterForm.tsx @@ -0,0 +1,42 @@ +'use client'; + +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@socialincome/ui'; +import { useState } from 'react'; + +interface FilterFormProps { + labels: string[]; + handleLabel: (label: string) => void; + filterText: string; +} + +export function FilterForm({ labels, handleLabel, filterText }: FilterFormProps) { + const [selectedLabel, setSelectedLabel] = useState(''); + + const handleChange = (value: string) => { + if (value === filterText) { + setSelectedLabel(''); + handleLabel(''); + } else { + setSelectedLabel(value); + handleLabel(value); + } + }; + + return ( +
+ +
+ ); +} diff --git a/website/src/app/[lang]/[region]/(website)/open-source/(components)/get-commits.ts b/website/src/app/[lang]/[region]/(website)/open-source/(components)/get-commits.ts new file mode 100644 index 000000000..c026f46a2 --- /dev/null +++ b/website/src/app/[lang]/[region]/(website)/open-source/(components)/get-commits.ts @@ -0,0 +1,91 @@ +const owner = 'socialincome-san'; +const repo = 'public'; + +interface GitHubCommit { + author: { + id: number; + login: string; + avatar_url: string; + } | null; + commit: { + author: { + date: string; + }; + }; +} + +export async function getCommits() { + // Calculate the date 30 days ago from today + const endDate = new Date().toISOString(); + const startDate = new Date(); + startDate.setDate(startDate.getDate() - 30); + const startDateISO = startDate.toISOString(); + + // Fetch recent commits from the last 30 days + const commitUrl = `https://api.github.com/repos/${owner}/${repo}/commits?since=${startDateISO}&until=${endDate}`; + const headers: Record = { + Accept: 'application/vnd.github+json', + }; + // Conditionally add the Authorization header if GITHUB_PAT is available + if (process.env.GITHUB_PAT) { + headers['Authorization'] = `Bearer ${process.env.GITHUB_PAT}`; + } + const res = await fetch(commitUrl, { + headers, + }); + + if (!res.ok) { + const errorDetails = await res.text(); + const status = res.status; + if (status === 403) { + throw new Error( + 'GitHub API rate limit exceeded. Please try again later or increase rate limit by authenticating.', + ); + } else if (status === 404) { + throw new Error(`GitHub repository ${owner}/${repo} not found.`); + } else { + throw new Error(`Failed to fetch recent commits from GitHub: ${status} - ${errorDetails}`); + } + } + + const recentCommits: GitHubCommit[] = await res.json(); + + // Fetch total commit count + const totalCommitsUrl = `https://api.github.com/repos/${owner}/${repo}/commits?per_page=1`; + const totalCommitsRes = await fetch(totalCommitsUrl, { + headers, + }); + + if (!totalCommitsRes.ok) { + const errorDetails = await totalCommitsRes.text(); + const status = totalCommitsRes.status; + if (status === 403) { + throw new Error(`GitHub API rate limit exceeded: ${status} - ${errorDetails}.`); + } else if (status === 404) { + throw new Error(`GitHub repository ${owner}/${repo} not found while fetching total commits.`); + } else { + throw new Error(`Failed to fetch total commits from GitHub: ${status} - ${errorDetails}`); + } + } + + // Extract the last page number from the Link header to get the total commit count + const linkHeader = totalCommitsRes.headers.get('link'); + // Default to 1 in case no Link header is provided + let totalCommits = 1; + + if (linkHeader) { + const match = linkHeader.match(/&page=(\d+)>; rel="last"/); + if (match) { + totalCommits = parseInt(match[1], 10); + } else { + console.warn('Link header exists but could not parse total commits page count.'); + } + } else { + console.warn('No Link header found; assuming a single commit.'); + } + + return { + totalCommits, + newCommits: recentCommits.length, + }; +} diff --git a/website/src/app/[lang]/[region]/(website)/open-source/(components)/get-contributors.ts b/website/src/app/[lang]/[region]/(website)/open-source/(components)/get-contributors.ts new file mode 100644 index 000000000..6341993ec --- /dev/null +++ b/website/src/app/[lang]/[region]/(website)/open-source/(components)/get-contributors.ts @@ -0,0 +1,63 @@ +const owner = 'socialincome-san'; +const repo = 'public'; + +interface Contributor { + id: number; + name: string; + commits: number; + avatarUrl: string; +} + +interface GitHubContributor { + author: { + id: number; + login: string; + avatar_url: string; + }; + total: number; +} + +export async function getContributors(): Promise { + const headers: Record = { + Accept: 'application/vnd.github+json', + }; + // Conditionally add the Authorization header if GITHUB_PAT is available + if (process.env.GITHUB_PAT) { + headers['Authorization'] = `Bearer ${process.env.GITHUB_PAT}`; + } + const res = await fetch(`https://api.github.com/repos/${owner}/${repo}/stats/contributors`, { + headers, + }); + + if (!res.ok) { + const errorDetails = await res.text(); + const status = res.status; + if (status === 403) { + throw new Error(`GitHub API rate limit exceeded: ${status} - ${errorDetails}.`); + } else if (status === 404) { + throw new Error(`GitHub repository ${owner}/${repo} not found.`); + } else { + throw new Error(`Failed to fetch contributors from GitHub: ${status} - ${errorDetails}`); + } + } + + const contributors = await res.json(); + + if (Object.keys(contributors).length === 0) { + console.warn('No contributor data available. The API returned an empty object.'); + return []; + } + + if (!Array.isArray(contributors)) { + throw new Error('Expected contributors to be an array, but received a different format.'); + } + + return contributors + .map((contributor: GitHubContributor) => ({ + id: contributor.author.id, + name: contributor.author.login, + commits: contributor.total, + avatarUrl: contributor.author.avatar_url, + })) + .sort((a: Contributor, b: Contributor) => b.commits - a.commits); +} diff --git a/website/src/app/[lang]/[region]/(website)/open-source/(components)/get-forks.ts b/website/src/app/[lang]/[region]/(website)/open-source/(components)/get-forks.ts new file mode 100644 index 000000000..34950a803 --- /dev/null +++ b/website/src/app/[lang]/[region]/(website)/open-source/(components)/get-forks.ts @@ -0,0 +1,81 @@ +const owner = 'socialincome-san'; +const repo = 'public'; + +interface GitHubFork { + id: number; + created_at: string; +} + +export async function getForkCount(): Promise<{ totalForks: number; newForks: number }> { + const repoUrl = `https://api.github.com/repos/${owner}/${repo}`; + const headers: Record = { + Accept: 'application/vnd.github+json', + }; + // Conditionally add the Authorization header if GITHUB_PAT is available + if (process.env.GITHUB_PAT) { + headers['Authorization'] = `Bearer ${process.env.GITHUB_PAT}`; + } + const repoRes = await fetch(repoUrl, { headers }); + + if (!repoRes.ok) { + const errorDetails = await repoRes.text(); + const status = repoRes.status; + if (status === 403) { + throw new Error(`GitHub API rate limit exceeded: ${status} - ${errorDetails}.`); + } else if (status === 404) { + throw new Error(`GitHub repository ${owner}/${repo} not found.`); + } else { + throw new Error(`Failed to fetch repository info from GitHub: ${status} - ${errorDetails}`); + } + } + + const repoData = await repoRes.json(); + const totalForks = repoData.forks_count; + + // Calculate the date 30 days ago from today + const startDate = new Date(); + startDate.setDate(startDate.getDate() - 30); + const startDateISO = startDate.toISOString(); + + // Fetch recent forks within the last 30 days + const forksUrl = `https://api.github.com/repos/${owner}/${repo}/forks?per_page=100`; + let newForks = 0; + let page = 1; + let hasMore = true; + + while (hasMore) { + const pagedRes = await fetch(`${forksUrl}&page=${page}`, { + headers, + }); + + if (!pagedRes.ok) { + const errorDetails = await pagedRes.text(); + const status = pagedRes.status; + + if (status === 403 && errorDetails.includes('API rate limit exceeded')) { + throw new Error( + 'GitHub API rate limit exceeded during forks fetching. Please try again later or increase rate limit by authenticating.', + ); + } else if (status === 404) { + throw new Error(`GitHub repository ${owner}/${repo} forks not found.`); + } else { + throw new Error(`Failed to fetch forks from GitHub: ${status} - ${errorDetails}`); + } + } + + const forks: GitHubFork[] = await pagedRes.json(); + + // Count new forks within the last 30 days + for (const fork of forks) { + if (new Date(fork.created_at) >= new Date(startDateISO)) { + newForks++; + } + } + + // No more pages if we got fewer than 100 forks + if (forks.length < 100) hasMore = false; + page++; + } + + return { totalForks, newForks }; +} diff --git a/website/src/app/[lang]/[region]/(website)/open-source/(components)/get-issues.ts b/website/src/app/[lang]/[region]/(website)/open-source/(components)/get-issues.ts new file mode 100644 index 000000000..7c44887e4 --- /dev/null +++ b/website/src/app/[lang]/[region]/(website)/open-source/(components)/get-issues.ts @@ -0,0 +1,82 @@ +const owner = 'socialincome-san'; +const repo = 'public'; + +interface Issue { + id: number; + url: string; + title: string; + labels: string[]; +} + +interface IssuesResponse { + issues: Issue[]; + labels: string[]; +} + +export async function getIssuesData(): Promise { + const headers: Record = { + Accept: 'application/vnd.github+json', + }; + if (process.env.GITHUB_PAT) { + headers['Authorization'] = `Bearer ${process.env.GITHUB_PAT}`; + } + + const issues: Issue[] = []; + const labels: string[] = []; + let page = 1; + let hasMore = true; + + while (hasMore) { + const res = await fetch( + `https://api.github.com/repos/${owner}/${repo}/issues?state=open&per_page=100&page=${page}`, + { headers }, + ); + + if (!res.ok) { + const errorDetails = await res.text(); + const status = res.status; + if (status === 403) { + throw new Error( + 'GitHub API rate limit exceeded. Please try again later or increase rate limit by authenticating.', + ); + } else if (status === 404) { + throw new Error(`GitHub repository ${owner}/${repo} not found.`); + } else { + throw new Error(`Failed to fetch issues from GitHub: ${status} - ${errorDetails}`); + } + } + + const data = await res.json(); + + // Break if no more issues + if (data.length === 0) break; + + // Exclude pull requests and map response to Issue interface + const filteredIssues = data + .filter((issue: any) => !issue.pull_request) + .map((issue: any) => { + const issueLabel = issue.labels.map((label: any) => label.name); + + issueLabel.forEach((label: string) => { + if (!labels.includes(label)) { + labels.push(label); + } + }); + + return { + id: issue.id, + url: issue.html_url, + title: issue.title, + labels: issueLabel, + }; + }); + + issues.push(...filteredIssues); + + // Stop if fewer than 100 items were returned, indicating the last page + hasMore = data.length === 100; + page++; + } + + return { issues, labels }; +} diff --git a/website/src/app/[lang]/[region]/(website)/open-source/(components)/get-stars.ts b/website/src/app/[lang]/[region]/(website)/open-source/(components)/get-stars.ts new file mode 100644 index 000000000..9f7cfa586 --- /dev/null +++ b/website/src/app/[lang]/[region]/(website)/open-source/(components)/get-stars.ts @@ -0,0 +1,87 @@ +const owner = 'socialincome-san'; +const repo = 'public'; + +interface GitHubStar { + user: { + id: number; + login: string; + }; + starred_at: string; +} + +export async function getStarCount(): Promise<{ totalStars: number; newStars: number }> { + const repoUrl = `https://api.github.com/repos/${owner}/${repo}`; + const headers: Record = { + Accept: 'application/vnd.github.star+json', + }; + // Conditionally add the Authorization header if GITHUB_PAT is available + if (process.env.GITHUB_PAT) { + headers['Authorization'] = `Bearer ${process.env.GITHUB_PAT}`; + } + const repoRes = await fetch(repoUrl, { + headers, + }); + + if (!repoRes.ok) { + const errorDetails = await repoRes.text(); + const status = repoRes.status; + if (status === 403) { + throw new Error(`GitHub API rate limit exceeded: ${status} - ${errorDetails}.`); + } else if (status === 404) { + throw new Error(`GitHub repository ${owner}/${repo} not found.`); + } else { + throw new Error(`Failed to fetch repository info from GitHub: ${status} - ${errorDetails}`); + } + } + + const repoData = await repoRes.json(); + const totalStars = repoData.stargazers_count; + + // Calculate the date 30 days ago from today + const startDate = new Date(); + startDate.setDate(startDate.getDate() - 30); + const startDateISO = startDate.toISOString(); + + // Fetch stargazers with timestamps within the last 30 days + const starUrl = `https://api.github.com/repos/${owner}/${repo}/stargazers?per_page=100`; + let newStars = 0; + let page = 1; + let hasMore = true; + + while (hasMore) { + const pagedRes = await fetch(`${starUrl}&page=${page}`, { + headers, + }); + + if (!pagedRes.ok) { + const errorDetails = await pagedRes.text(); + const status = pagedRes.status; + + if (status === 403 && errorDetails.includes('API rate limit exceeded')) { + throw new Error( + 'GitHub API rate limit exceeded during stargazers fetching. Please try again later or increase rate limit by authenticating.', + ); + } else if (status === 404) { + throw new Error(`GitHub repository ${owner}/${repo} stargazers not found.`); + } else { + throw new Error(`Failed to fetch stargazers from GitHub: ${status} - ${errorDetails}`); + } + } + + const stars: GitHubStar[] = await pagedRes.json(); + + // Count new stars within the last 30 days + for (const star of stars) { + const starredAt = new Date(star.starred_at); + if (starredAt >= new Date(startDateISO)) { + newStars++; + } + } + + // No more pages if we got fewer than 100 stars + if (stars.length < 100) hasMore = false; + page++; + } + + return { totalStars, newStars }; +} diff --git a/website/src/app/[lang]/[region]/(website)/open-source/(components)/issue.tsx b/website/src/app/[lang]/[region]/(website)/open-source/(components)/issue.tsx new file mode 100644 index 000000000..8c56dcea6 --- /dev/null +++ b/website/src/app/[lang]/[region]/(website)/open-source/(components)/issue.tsx @@ -0,0 +1,30 @@ +'use client'; + +import { Button, TableCell, TableRow } from '@socialincome/ui'; +import Link from 'next/link'; + +interface Issue { + id: number; + title: string; + url: string; + labels: string[]; +} + +interface IssueProps extends Pick { + text: string; +} + +export function Issue({ title, url, text }: IssueProps) { + return ( + + {title} + + + + + ); +} diff --git a/website/src/app/[lang]/[region]/(website)/open-source/(components)/issues-client.tsx b/website/src/app/[lang]/[region]/(website)/open-source/(components)/issues-client.tsx new file mode 100644 index 000000000..f8fd46e11 --- /dev/null +++ b/website/src/app/[lang]/[region]/(website)/open-source/(components)/issues-client.tsx @@ -0,0 +1,76 @@ +'use client'; + +import { Button, Table, TableBody, TableCell, TableHeader, TableRow, Typography } from '@socialincome/ui'; +import { useState } from 'react'; +import { FilterForm } from './filterForm'; +import { Issue } from './issue'; + +interface Issue { + id: number; + title: string; + url: string; + labels: string[]; +} + +interface IssueClientProps { + title: string; + issues: Issue[]; + labels: string[]; + tableHeader: string; + linkText: string; + filterText: string; +} + +export function IssueClient({ title, issues, labels, tableHeader, filterText, linkText }: IssueClientProps) { + const [filteredLabel, setFilteredLabel] = useState(''); + const [showAllIssues, setShowAllIssues] = useState(false); + + // Filter issues based on the selected label + const filteredIssues = filteredLabel ? issues.filter((issue) => issue.labels.includes(filteredLabel)) : issues; + + // Determine issues to display based on "show all" state + const displayedIssues = showAllIssues ? filteredIssues : filteredIssues.slice(0, 6); + + const handleFilterChange = (label: string) => { + if (label === filterText) { + // Show all issues when filterText is selected + setFilteredLabel(''); + setShowAllIssues(true); + } else { + setFilteredLabel(label); + setShowAllIssues(false); + } + }; + + return ( +
+ + {title} + + +
+ + + + {tableHeader} + + + + {displayedIssues.map((issue) => ( + + ))} + +
+
+ {!showAllIssues && filteredIssues.length > 6 && ( +
+ +
+ )} +
+ ); +} diff --git a/website/src/app/[lang]/[region]/(website)/open-source/(sections)/contributors.tsx b/website/src/app/[lang]/[region]/(website)/open-source/(sections)/contributors.tsx new file mode 100644 index 000000000..a85670028 --- /dev/null +++ b/website/src/app/[lang]/[region]/(website)/open-source/(sections)/contributors.tsx @@ -0,0 +1,27 @@ +import { DefaultParams } from '@/app/[lang]/[region]'; +import { Translator } from '@socialincome/shared/src/utils/i18n'; +import { OpenSourceContributorsClient } from '../(components)/contributors-client'; +import { getContributors } from '../(components)/get-contributors'; + +type Metadata = { + heading: string; +}; + +export async function OpenSourceContributors({ lang }: DefaultParams) { + const translator = await Translator.getInstance({ + language: lang, + namespaces: ['website-open-source'], + }); + + const metadata: Metadata = translator.t('metadata'); + const heading = metadata.heading; + + const contributors = await getContributors(); + const totalContributors = contributors.length; + + return ( + + ); +} + +export default OpenSourceContributors; diff --git a/website/src/app/[lang]/[region]/(website)/open-source/(sections)/hero.tsx b/website/src/app/[lang]/[region]/(website)/open-source/(sections)/hero.tsx new file mode 100644 index 000000000..36950aaed --- /dev/null +++ b/website/src/app/[lang]/[region]/(website)/open-source/(sections)/hero.tsx @@ -0,0 +1,26 @@ +import { DefaultParams } from '@/app/[lang]/[region]'; +import { Translator } from '@socialincome/shared/src/utils/i18n'; +import { Typography } from '@socialincome/ui'; +import { FontColor } from '@socialincome/ui/src/interfaces/color'; + +export async function Hero({ lang }: DefaultParams) { + const translator = await Translator.getInstance({ + language: lang, + namespaces: ['website-open-source'], + }); + + return ( +
+
+ {translator.t<{ text: string; color?: FontColor }[]>('hero.title-1').map((title, index) => ( + + {title.text}{' '} + + ))} +
+ + {translator.t('hero.subtitle')} + +
+ ); +} diff --git a/website/src/app/[lang]/[region]/(website)/open-source/(sections)/open-issues.tsx b/website/src/app/[lang]/[region]/(website)/open-source/(sections)/open-issues.tsx new file mode 100644 index 000000000..5a5f02771 --- /dev/null +++ b/website/src/app/[lang]/[region]/(website)/open-source/(sections)/open-issues.tsx @@ -0,0 +1,37 @@ +import { DefaultParams } from '@/app/[lang]/[region]'; +import { Translator } from '@socialincome/shared/src/utils/i18n'; +import { getIssuesData } from '../(components)/get-issues'; +import { IssueClient } from '../(components)/issues-client'; + +type Issues = { + title: string; + header: string; + link: string; + filter: string; +}; + +export async function OpenIssues({ lang }: DefaultParams) { + const translator = await Translator.getInstance({ + language: lang, + namespaces: ['website-open-source'], + }); + + const issuesMeta: Issues = translator.t('issues'); + const title = issuesMeta.title; + const tableHeader = issuesMeta.header; + const linkText = issuesMeta.link; + const filterText = issuesMeta.filter; + + const { issues, labels } = await getIssuesData(); + + return ( + + ); +} diff --git a/website/src/app/[lang]/[region]/(website)/open-source/(sections)/overview.tsx b/website/src/app/[lang]/[region]/(website)/open-source/(sections)/overview.tsx new file mode 100644 index 000000000..1b68d451b --- /dev/null +++ b/website/src/app/[lang]/[region]/(website)/open-source/(sections)/overview.tsx @@ -0,0 +1,96 @@ +import { DefaultParams } from '@/app/[lang]/[region]'; +import { Translator } from '@socialincome/shared/src/utils/i18n'; +import { Card, Typography } from '@socialincome/ui'; +import { getCommits } from '../(components)/get-commits'; +import { getForkCount } from '../(components)/get-forks'; +import { getStarCount } from '../(components)/get-stars'; + +type OverviewProp = { + title: string; + total: number; + time: string; +}; + +interface OverviewItem { + title: string; + time: string; +} + +interface Overview { + forks: OverviewItem; + commits: OverviewItem; + stars: OverviewItem; +} + +export function OverviewCard({ title, total, time }: OverviewProp) { + return ( + + {/* Large screens layout */} +
+ {/* Number on the left */} +
+ + {total} + +
+ + {/* Title and time on the right */} +
+ + {title} + + + {time} + +
+
+ + {/* Medium and smaller screens layout */} +
+ + {title} + + + {total} + + + {time} + +
+
+ ); +} + +export async function Overview({ lang }: DefaultParams) { + const translator = await Translator.getInstance({ + language: lang, + namespaces: ['website-open-source'], + }); + + const { totalForks, newForks } = await getForkCount(); + const { totalStars, newStars } = await getStarCount(); + const { newCommits, totalCommits } = await getCommits(); + const { forks, commits, stars }: Overview = translator.t('overview'); + + return ( +
+
+ 0 ? `+${newCommits} last month` : `${newCommits} last month`} + /> + 0 ? `+${newStars} last month` : `${newStars} last month`} + /> + 0 ? `+${newForks} last month` : `${newForks} last month`} + /> +
+
+ ); +} diff --git a/website/src/app/[lang]/[region]/(website)/open-source/page.tsx b/website/src/app/[lang]/[region]/(website)/open-source/page.tsx new file mode 100644 index 000000000..0c1e62ca2 --- /dev/null +++ b/website/src/app/[lang]/[region]/(website)/open-source/page.tsx @@ -0,0 +1,17 @@ +import { DefaultPageProps } from '@/app/[lang]/[region]'; +import { BaseContainer } from '@socialincome/ui'; +import { OpenSourceContributors } from './(sections)/contributors'; +import { Hero } from './(sections)/hero'; +import { OpenIssues } from './(sections)/open-issues'; +import { Overview } from './(sections)/overview'; + +export default async function Page({ params: { lang, region } }: DefaultPageProps) { + return ( + + + + + + + ); +} diff --git a/website/src/components/logos/si-logo-animation.json b/website/src/components/logos/si-logo-animation.json index 5255dd546..957ed09e7 100644 --- a/website/src/components/logos/si-logo-animation.json +++ b/website/src/components/logos/si-logo-animation.json @@ -1 +1,1032 @@ -{"v":"5.8.1","fr":60,"ip":0,"op":170,"w":2300,"h":248,"nm":"Social Income Animation 2 (Knapp erangeschnitten)","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Socia","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":86,"s":[498.95,123.868,0],"to":[0,0,0],"ti":[0,0,0]},{"t":111,"s":[411.95,123.868,0]}],"ix":2,"l":2},"a":{"a":0,"k":[411.45,122.868,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,53.186],[-49.246,0],[-0.492,-38.904],[0,0],[16.744,0],[0,-32.009],[-20.683,0],[-0.492,18.713],[0,0],[45.306,0]],"o":[[0,-53.678],[47.768,0],[0,0],[-1.969,-19.698],[-21.176,0],[0,32.01],[17.236,0],[0,0],[0,38.904],[-49.246,0]],"v":[[399.877,157.094],[481.133,68.452],[557.464,133.949],[510.188,133.949],[481.133,102.924],[447.153,156.109],[481.133,210.773],[509.695,179.748],[557.464,179.748],[481.133,245.737]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[0,53.679],[-52.201,0],[-0.492,-53.186],[52.2,0]],"o":[[0,-53.678],[52.2,0],[0,53.679],[-52.201,0]],"v":[[208.803,157.094],[293.999,68.452],[379.686,157.094],[293.999,245.737]],"c":true},"ix":2},"nm":"Path 2","mn":"ADBE Vector Shape - Group","hd":false},{"ind":2,"ty":"sh","ix":3,"ks":{"a":0,"k":{"i":[[0,-33.487],[-24.623,0],[-0.492,32.995],[24.622,0]],"o":[[0,32.995],[24.622,0],[0,-33.98],[-24.623,0]],"v":[[256.571,157.094],[293.999,210.773],[331.917,157.094],[293.999,103.416]],"c":true},"ix":2},"nm":"Path 3","mn":"ADBE Vector Shape - Group","hd":false},{"ind":3,"ty":"sh","ix":4,"ks":{"a":0,"k":{"i":[[0,52.201],[0,0],[-28.07,0],[0,17.729],[30.533,8.865],[0,0],[0,30.532],[-52.693,0],[-1.478,-48.261],[0,0],[25.115,0],[0,-15.758],[-30.04,-8.864],[0,0],[0,-33.487],[54.662,0]],"o":[[0,0],[0,28.07],[24.131,0],[0,-15.759],[0,0],[-33.98,-9.849],[0,-41.859],[52.693,0],[0,0],[0,-23.145],[-20.684,0],[0,14.282],[0,0],[41.367,12.312],[0,45.306],[-58.603,0]],"v":[[0,161.034],[51.215,161.034],[97.507,207.326],[137.396,178.27],[96.522,145.768],[57.618,134.441],[4.432,69.929],[94.06,0],[181.225,78.793],[130.994,78.793],[90.613,37.92],[54.663,65.004],[92.582,94.06],[126.069,103.909],[187.627,169.898],[96.522,245.737]],"c":true},"ix":2},"nm":"Path 4","mn":"ADBE Vector Shape - Group","hd":false},{"ind":4,"ty":"sh","ix":5,"ks":{"a":0,"k":{"i":[[0,32.995],[-51.215,5.417],[0,0],[16.744,0],[0.985,-14.774],[0,0],[-46.292,0],[-12.803,-12.311],[0,-31.025],[0,0],[0,0],[-3.447,-13.789],[0,0],[0.492,6.402],[27.578,0]],"o":[[0,-49.738],[0,0],[0,-16.744],[-19.205,0],[0,0],[1.97,-36.442],[24.622,0],[13.297,12.804],[0,0],[0,0],[0,23.639],[0,0],[-1.478,-7.88],[-13.789,15.758],[-36.934,0]],"v":[[662.358,194.029],[773.654,134.441],[773.654,126.069],[746.568,99.969],[716.036,124.592],[668.759,124.592],[747.061,68.452],[802.216,86.18],[817.975,144.291],[818.467,144.291],[818.467,181.717],[822.9,239.827],[781.533,239.827],[779.071,218.652],[723.423,243.274]],"c":true},"ix":2},"nm":"Path 5","mn":"ADBE Vector Shape - Group","hd":false},{"ind":5,"ty":"sh","ix":6,"ks":{"a":0,"k":{"i":[[0,-24.623],[-14.773,0],[0,30.04],[0,0]],"o":[[0,12.803],[24.131,0],[0,0],[-32.503,3.94]],"v":[[710.126,193.537],[735.734,213.235],[773.654,169.898],[773.654,164.481]],"c":true},"ix":2},"nm":"Path 6","mn":"ADBE Vector Shape - Group","hd":false},{"ind":6,"ty":"sh","ix":7,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[587.012,241.305],[587.012,73.376],[632.81,73.376],[632.81,241.305]],"c":true},"ix":2},"nm":"Path 7","mn":"ADBE Vector Shape - Group","hd":false},{"ind":7,"ty":"sh","ix":8,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[587.012,49.246],[587.012,4.925],[632.81,4.925],[632.81,49.246]],"c":true},"ix":2},"nm":"Path 8","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.980392156863,0.78431372549,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Vereinigungsmenge 1","np":10,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-1,"op":3600,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"L 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":41,"s":[-90]},{"i":{"x":[0.968],"y":[0.694]},"o":{"x":[0.478],"y":[0.002]},"t":84,"s":[-90]},{"t":120,"s":[-40]}],"ix":10},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":86,"s":[993.218,245.215,0],"to":[0,0,0],"ti":[0,0,0]},{"t":111,"s":[882.218,245.215,0]}],"ix":2,"l":2},"a":{"a":0,"k":[-117.944,23.884,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[235.888,47.769],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.980392156863,0.78431372549,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Rechteck 23","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":3641,"st":41,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"L 2","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":84,"s":[0]},{"t":114,"s":[100]}],"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":41,"s":[-90]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":111,"s":[-90]},{"t":126,"s":[-65]}],"ix":10},"p":{"a":0,"k":[1075.063,244.956,0],"ix":2,"l":2},"a":{"a":0,"k":[-117.944,23.884,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[235.888,47.769],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.980392156863,0.78431372549,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Rechteck 22","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":62,"op":3641,"st":41,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Income","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.333,"y":0},"t":86,"s":[1657.916,126.331,0],"to":[0,0,0],"ti":[0,0,0]},{"t":111,"s":[1752.916,126.331,0]}],"ix":2,"l":2},"a":{"a":0,"k":[545.891,120.406,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,53.678],[-52.2,0],[0,-53.186],[52.201,0]],"o":[[0,-53.679],[52.694,0],[0,53.678],[-52.2,0]],"v":[[448.138,152.17],[533.333,63.527],[619.021,152.17],[533.333,240.813]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[0,-33.487],[-24.622,0],[0,32.995],[24.623,0]],"o":[[0,32.995],[24.623,0],[0,-33.98],[-24.622,0]],"v":[[495.906,152.17],[533.333,205.848],[571.253,152.17],[533.333,98.492]],"c":true},"ix":2},"nm":"Path 2","mn":"ADBE Vector Shape - Group","hd":false},{"ind":2,"ty":"sh","ix":3,"ks":{"a":0,"k":{"i":[[0,53.186],[-49.246,0],[-0.492,-38.904],[0,0],[16.743,0],[0,-32.009],[-20.684,0],[-0.492,18.714],[0,0],[45.306,0]],"o":[[0,-53.679],[47.768,0],[0,0],[-1.97,-19.698],[-21.176,0],[0,32.01],[17.236,0],[0,0],[0,38.904],[-49.246,0]],"v":[[270.852,152.17],[352.109,63.527],[428.44,129.024],[381.164,129.024],[352.109,97.999],[318.128,151.185],[352.109,205.848],[380.671,174.823],[428.44,174.823],[352.109,240.813]],"c":true},"ix":2},"nm":"Path 3","mn":"ADBE Vector Shape - Group","hd":false},{"ind":3,"ty":"sh","ix":4,"ks":{"a":0,"k":{"i":[[0,54.663],[-51.215,0],[0,-56.14],[0.985,-4.432],[0,0],[-27.086,0],[-4.925,13.296],[0,0],[42.845,0]],"o":[[0,-50.231],[51.709,0],[0,1.477],[0,0],[0,24.623],[15.759,0],[0,0],[-9.85,36.442],[-55.648,0]],"v":[[929.763,150.692],[1012.989,63.527],[1091.782,155.125],[1090.797,164.481],[976.055,164.481],[1012.989,207.818],[1042.537,185.657],[1090.797,185.657],[1013.481,240.32]],"c":true},"ix":2},"nm":"Path 4","mn":"ADBE Vector Shape - Group","hd":false},{"ind":4,"ty":"sh","ix":5,"ks":{"a":0,"k":{"i":[[0.985,-24.131],[0,0],[18.714,0]],"o":[[0,0],[-0.985,-24.131],[-18.714,0]],"v":[[974.577,133.456],[1045.491,133.456],[1010.526,95.537]],"c":true},"ix":2},"nm":"Path 5","mn":"ADBE Vector Shape - Group","hd":false},{"ind":5,"ty":"sh","ix":6,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[17.236,0],[0,-36.934],[0,0],[0,0],[0,0],[16.743,0],[0,-36.934],[0,0],[0,0],[0,0],[0,0],[0,0],[-22.161,0],[-9.849,-19.206],[-24.13,0],[-0.492,-41.367],[0,0]],"o":[[0,0],[0,-21.668],[-21.668,0],[0,0],[0,0],[0,0],[0,-21.668],[-22.161,0],[0,0],[0,0],[0,0],[0,0],[0,0],[10.342,-16.251],[23.638,0],[11.327,-18.714],[42.845,0],[0,0],[0,0]],"v":[[855.401,236.38],[855.401,133.949],[830.779,99.969],[798.769,153.647],[798.769,236.38],[752.971,236.38],[752.971,133.949],[727.855,99.969],[695.845,153.647],[695.845,235.888],[650.047,235.888],[650.047,67.959],[690.428,67.959],[689.936,91.598],[742.629,65.497],[790.889,91.598],[846.537,65.497],[901.201,133.949],[901.201,236.38]],"c":true},"ix":2},"nm":"Path 6","mn":"ADBE Vector Shape - Group","hd":false},{"ind":6,"ty":"sh","ix":7,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[22.653,0],[0,-38.904],[0,0],[0,0],[0,0],[0,0],[0,0],[-21.175,0],[0.492,-43.336],[0,0]],"o":[[0,0],[0,-24.622],[-21.176,0],[0,0],[0,0],[0,0],[0,0],[0,0],[12.312,-15.759],[47.768,0],[0,0],[0,0]],"v":[[196.492,236.38],[196.492,143.305],[169.406,99.969],[136.411,153.155],[136.411,236.38],[90.613,236.38],[90.613,68.452],[133.456,68.452],[133.456,90.613],[186.642,65.497],[242.29,142.813],[242.29,236.38]],"c":true},"ix":2},"nm":"Path 7","mn":"ADBE Vector Shape - Group","hd":false},{"ind":7,"ty":"sh","ix":8,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[0,235.888],[0,0],[47.768,0],[47.768,235.888]],"c":true},"ix":2},"nm":"Path 8","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.980392156863,0.78431372549,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Vereinigungsmenge 2","np":10,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":3641,"st":41,"bm":0}],"markers":[]} \ No newline at end of file +{ + "v": "5.8.1", + "fr": 60, + "ip": 0, + "op": 170, + "w": 2300, + "h": 248, + "nm": "Social Income Animation 2 (Knapp erangeschnitten)", + "ddd": 0, + "assets": [], + "layers": [ + { + "ddd": 0, + "ind": 1, + "ty": 4, + "nm": "Socia", + "sr": 1, + "ks": { + "o": { "a": 0, "k": 100, "ix": 11 }, + "r": { "a": 0, "k": 0, "ix": 10 }, + "p": { + "a": 1, + "k": [ + { + "i": { "x": 0.667, "y": 1 }, + "o": { "x": 0.333, "y": 0 }, + "t": 86, + "s": [498.95, 123.868, 0], + "to": [0, 0, 0], + "ti": [0, 0, 0] + }, + { "t": 111, "s": [411.95, 123.868, 0] } + ], + "ix": 2, + "l": 2 + }, + "a": { "a": 0, "k": [411.45, 122.868, 0], "ix": 1, "l": 2 }, + "s": { "a": 0, "k": [100, 100, 100], "ix": 6, "l": 2 } + }, + "ao": 0, + "shapes": [ + { + "ty": "gr", + "it": [ + { + "ind": 0, + "ty": "sh", + "ix": 1, + "ks": { + "a": 0, + "k": { + "i": [ + [0, 53.186], + [-49.246, 0], + [-0.492, -38.904], + [0, 0], + [16.744, 0], + [0, -32.009], + [-20.683, 0], + [-0.492, 18.713], + [0, 0], + [45.306, 0] + ], + "o": [ + [0, -53.678], + [47.768, 0], + [0, 0], + [-1.969, -19.698], + [-21.176, 0], + [0, 32.01], + [17.236, 0], + [0, 0], + [0, 38.904], + [-49.246, 0] + ], + "v": [ + [399.877, 157.094], + [481.133, 68.452], + [557.464, 133.949], + [510.188, 133.949], + [481.133, 102.924], + [447.153, 156.109], + [481.133, 210.773], + [509.695, 179.748], + [557.464, 179.748], + [481.133, 245.737] + ], + "c": true + }, + "ix": 2 + }, + "nm": "Path 1", + "mn": "ADBE Vector Shape - Group", + "hd": false + }, + { + "ind": 1, + "ty": "sh", + "ix": 2, + "ks": { + "a": 0, + "k": { + "i": [ + [0, 53.679], + [-52.201, 0], + [-0.492, -53.186], + [52.2, 0] + ], + "o": [ + [0, -53.678], + [52.2, 0], + [0, 53.679], + [-52.201, 0] + ], + "v": [ + [208.803, 157.094], + [293.999, 68.452], + [379.686, 157.094], + [293.999, 245.737] + ], + "c": true + }, + "ix": 2 + }, + "nm": "Path 2", + "mn": "ADBE Vector Shape - Group", + "hd": false + }, + { + "ind": 2, + "ty": "sh", + "ix": 3, + "ks": { + "a": 0, + "k": { + "i": [ + [0, -33.487], + [-24.623, 0], + [-0.492, 32.995], + [24.622, 0] + ], + "o": [ + [0, 32.995], + [24.622, 0], + [0, -33.98], + [-24.623, 0] + ], + "v": [ + [256.571, 157.094], + [293.999, 210.773], + [331.917, 157.094], + [293.999, 103.416] + ], + "c": true + }, + "ix": 2 + }, + "nm": "Path 3", + "mn": "ADBE Vector Shape - Group", + "hd": false + }, + { + "ind": 3, + "ty": "sh", + "ix": 4, + "ks": { + "a": 0, + "k": { + "i": [ + [0, 52.201], + [0, 0], + [-28.07, 0], + [0, 17.729], + [30.533, 8.865], + [0, 0], + [0, 30.532], + [-52.693, 0], + [-1.478, -48.261], + [0, 0], + [25.115, 0], + [0, -15.758], + [-30.04, -8.864], + [0, 0], + [0, -33.487], + [54.662, 0] + ], + "o": [ + [0, 0], + [0, 28.07], + [24.131, 0], + [0, -15.759], + [0, 0], + [-33.98, -9.849], + [0, -41.859], + [52.693, 0], + [0, 0], + [0, -23.145], + [-20.684, 0], + [0, 14.282], + [0, 0], + [41.367, 12.312], + [0, 45.306], + [-58.603, 0] + ], + "v": [ + [0, 161.034], + [51.215, 161.034], + [97.507, 207.326], + [137.396, 178.27], + [96.522, 145.768], + [57.618, 134.441], + [4.432, 69.929], + [94.06, 0], + [181.225, 78.793], + [130.994, 78.793], + [90.613, 37.92], + [54.663, 65.004], + [92.582, 94.06], + [126.069, 103.909], + [187.627, 169.898], + [96.522, 245.737] + ], + "c": true + }, + "ix": 2 + }, + "nm": "Path 4", + "mn": "ADBE Vector Shape - Group", + "hd": false + }, + { + "ind": 4, + "ty": "sh", + "ix": 5, + "ks": { + "a": 0, + "k": { + "i": [ + [0, 32.995], + [-51.215, 5.417], + [0, 0], + [16.744, 0], + [0.985, -14.774], + [0, 0], + [-46.292, 0], + [-12.803, -12.311], + [0, -31.025], + [0, 0], + [0, 0], + [-3.447, -13.789], + [0, 0], + [0.492, 6.402], + [27.578, 0] + ], + "o": [ + [0, -49.738], + [0, 0], + [0, -16.744], + [-19.205, 0], + [0, 0], + [1.97, -36.442], + [24.622, 0], + [13.297, 12.804], + [0, 0], + [0, 0], + [0, 23.639], + [0, 0], + [-1.478, -7.88], + [-13.789, 15.758], + [-36.934, 0] + ], + "v": [ + [662.358, 194.029], + [773.654, 134.441], + [773.654, 126.069], + [746.568, 99.969], + [716.036, 124.592], + [668.759, 124.592], + [747.061, 68.452], + [802.216, 86.18], + [817.975, 144.291], + [818.467, 144.291], + [818.467, 181.717], + [822.9, 239.827], + [781.533, 239.827], + [779.071, 218.652], + [723.423, 243.274] + ], + "c": true + }, + "ix": 2 + }, + "nm": "Path 5", + "mn": "ADBE Vector Shape - Group", + "hd": false + }, + { + "ind": 5, + "ty": "sh", + "ix": 6, + "ks": { + "a": 0, + "k": { + "i": [ + [0, -24.623], + [-14.773, 0], + [0, 30.04], + [0, 0] + ], + "o": [ + [0, 12.803], + [24.131, 0], + [0, 0], + [-32.503, 3.94] + ], + "v": [ + [710.126, 193.537], + [735.734, 213.235], + [773.654, 169.898], + [773.654, 164.481] + ], + "c": true + }, + "ix": 2 + }, + "nm": "Path 6", + "mn": "ADBE Vector Shape - Group", + "hd": false + }, + { + "ind": 6, + "ty": "sh", + "ix": 7, + "ks": { + "a": 0, + "k": { + "i": [ + [0, 0], + [0, 0], + [0, 0], + [0, 0] + ], + "o": [ + [0, 0], + [0, 0], + [0, 0], + [0, 0] + ], + "v": [ + [587.012, 241.305], + [587.012, 73.376], + [632.81, 73.376], + [632.81, 241.305] + ], + "c": true + }, + "ix": 2 + }, + "nm": "Path 7", + "mn": "ADBE Vector Shape - Group", + "hd": false + }, + { + "ind": 7, + "ty": "sh", + "ix": 8, + "ks": { + "a": 0, + "k": { + "i": [ + [0, 0], + [0, 0], + [0, 0], + [0, 0] + ], + "o": [ + [0, 0], + [0, 0], + [0, 0], + [0, 0] + ], + "v": [ + [587.012, 49.246], + [587.012, 4.925], + [632.81, 4.925], + [632.81, 49.246] + ], + "c": true + }, + "ix": 2 + }, + "nm": "Path 8", + "mn": "ADBE Vector Shape - Group", + "hd": false + }, + { + "ty": "fl", + "c": { "a": 0, "k": [0.980392156863, 0.78431372549, 0, 1], "ix": 4 }, + "o": { "a": 0, "k": 100, "ix": 5 }, + "r": 1, + "bm": 0, + "nm": "Fill 1", + "mn": "ADBE Vector Graphic - Fill", + "hd": false + }, + { + "ty": "tr", + "p": { "a": 0, "k": [0, 0], "ix": 2 }, + "a": { "a": 0, "k": [0, 0], "ix": 1 }, + "s": { "a": 0, "k": [100, 100], "ix": 3 }, + "r": { "a": 0, "k": 0, "ix": 6 }, + "o": { "a": 0, "k": 100, "ix": 7 }, + "sk": { "a": 0, "k": 0, "ix": 4 }, + "sa": { "a": 0, "k": 0, "ix": 5 }, + "nm": "Transformieren" + } + ], + "nm": "Vereinigungsmenge 1", + "np": 10, + "cix": 2, + "bm": 0, + "ix": 1, + "mn": "ADBE Vector Group", + "hd": false + } + ], + "ip": -1, + "op": 3600, + "st": 0, + "bm": 0 + }, + { + "ddd": 0, + "ind": 2, + "ty": 4, + "nm": "L 1", + "sr": 1, + "ks": { + "o": { "a": 0, "k": 100, "ix": 11 }, + "r": { + "a": 1, + "k": [ + { "i": { "x": [0.667], "y": [1] }, "o": { "x": [0.167], "y": [0] }, "t": 41, "s": [-90] }, + { "i": { "x": [0.968], "y": [0.694] }, "o": { "x": [0.478], "y": [0.002] }, "t": 84, "s": [-90] }, + { "t": 120, "s": [-40] } + ], + "ix": 10 + }, + "p": { + "a": 1, + "k": [ + { + "i": { "x": 0.833, "y": 0.833 }, + "o": { "x": 0.167, "y": 0.167 }, + "t": 86, + "s": [993.218, 245.215, 0], + "to": [0, 0, 0], + "ti": [0, 0, 0] + }, + { "t": 111, "s": [882.218, 245.215, 0] } + ], + "ix": 2, + "l": 2 + }, + "a": { "a": 0, "k": [-117.944, 23.884, 0], "ix": 1, "l": 2 }, + "s": { "a": 0, "k": [100, 100, 100], "ix": 6, "l": 2 } + }, + "ao": 0, + "shapes": [ + { + "ty": "gr", + "it": [ + { + "ty": "rc", + "d": 1, + "s": { "a": 0, "k": [235.888, 47.769], "ix": 2 }, + "p": { "a": 0, "k": [0, 0], "ix": 3 }, + "r": { "a": 0, "k": 0, "ix": 4 }, + "nm": "Rectangle Path 1", + "mn": "ADBE Vector Shape - Rect", + "hd": false + }, + { + "ty": "fl", + "c": { "a": 0, "k": [0.980392156863, 0.78431372549, 0, 1], "ix": 4 }, + "o": { "a": 0, "k": 100, "ix": 5 }, + "r": 1, + "bm": 0, + "nm": "Fill 1", + "mn": "ADBE Vector Graphic - Fill", + "hd": false + }, + { + "ty": "tr", + "p": { "a": 0, "k": [0, 0], "ix": 2 }, + "a": { "a": 0, "k": [0, 0], "ix": 1 }, + "s": { "a": 0, "k": [100, 100], "ix": 3 }, + "r": { "a": 0, "k": 0, "ix": 6 }, + "o": { "a": 0, "k": 100, "ix": 7 }, + "sk": { "a": 0, "k": 0, "ix": 4 }, + "sa": { "a": 0, "k": 0, "ix": 5 }, + "nm": "Transformieren" + } + ], + "nm": "Rechteck 23", + "np": 3, + "cix": 2, + "bm": 0, + "ix": 1, + "mn": "ADBE Vector Group", + "hd": false + } + ], + "ip": 0, + "op": 3641, + "st": 41, + "bm": 0 + }, + { + "ddd": 0, + "ind": 3, + "ty": 4, + "nm": "L 2", + "sr": 1, + "ks": { + "o": { + "a": 1, + "k": [ + { "i": { "x": [0.833], "y": [0.833] }, "o": { "x": [0.167], "y": [0.167] }, "t": 84, "s": [0] }, + { "t": 114, "s": [100] } + ], + "ix": 11 + }, + "r": { + "a": 1, + "k": [ + { "i": { "x": [0.667], "y": [1] }, "o": { "x": [0.167], "y": [0] }, "t": 41, "s": [-90] }, + { "i": { "x": [0.667], "y": [1] }, "o": { "x": [0.333], "y": [0] }, "t": 111, "s": [-90] }, + { "t": 126, "s": [-65] } + ], + "ix": 10 + }, + "p": { "a": 0, "k": [1075.063, 244.956, 0], "ix": 2, "l": 2 }, + "a": { "a": 0, "k": [-117.944, 23.884, 0], "ix": 1, "l": 2 }, + "s": { "a": 0, "k": [100, 100, 100], "ix": 6, "l": 2 } + }, + "ao": 0, + "shapes": [ + { + "ty": "gr", + "it": [ + { + "ty": "rc", + "d": 1, + "s": { "a": 0, "k": [235.888, 47.769], "ix": 2 }, + "p": { "a": 0, "k": [0, 0], "ix": 3 }, + "r": { "a": 0, "k": 0, "ix": 4 }, + "nm": "Rectangle Path 1", + "mn": "ADBE Vector Shape - Rect", + "hd": false + }, + { + "ty": "fl", + "c": { "a": 0, "k": [0.980392156863, 0.78431372549, 0, 1], "ix": 4 }, + "o": { "a": 0, "k": 100, "ix": 5 }, + "r": 1, + "bm": 0, + "nm": "Fill 1", + "mn": "ADBE Vector Graphic - Fill", + "hd": false + }, + { + "ty": "tr", + "p": { "a": 0, "k": [0, 0], "ix": 2 }, + "a": { "a": 0, "k": [0, 0], "ix": 1 }, + "s": { "a": 0, "k": [100, 100], "ix": 3 }, + "r": { "a": 0, "k": 0, "ix": 6 }, + "o": { "a": 0, "k": 100, "ix": 7 }, + "sk": { "a": 0, "k": 0, "ix": 4 }, + "sa": { "a": 0, "k": 0, "ix": 5 }, + "nm": "Transformieren" + } + ], + "nm": "Rechteck 22", + "np": 3, + "cix": 2, + "bm": 0, + "ix": 1, + "mn": "ADBE Vector Group", + "hd": false + } + ], + "ip": 62, + "op": 3641, + "st": 41, + "bm": 0 + }, + { + "ddd": 0, + "ind": 4, + "ty": 4, + "nm": "Income", + "sr": 1, + "ks": { + "o": { "a": 0, "k": 100, "ix": 11 }, + "r": { "a": 0, "k": 0, "ix": 10 }, + "p": { + "a": 1, + "k": [ + { + "i": { "x": 0.833, "y": 0.833 }, + "o": { "x": 0.333, "y": 0 }, + "t": 86, + "s": [1657.916, 126.331, 0], + "to": [0, 0, 0], + "ti": [0, 0, 0] + }, + { "t": 111, "s": [1752.916, 126.331, 0] } + ], + "ix": 2, + "l": 2 + }, + "a": { "a": 0, "k": [545.891, 120.406, 0], "ix": 1, "l": 2 }, + "s": { "a": 0, "k": [100, 100, 100], "ix": 6, "l": 2 } + }, + "ao": 0, + "shapes": [ + { + "ty": "gr", + "it": [ + { + "ind": 0, + "ty": "sh", + "ix": 1, + "ks": { + "a": 0, + "k": { + "i": [ + [0, 53.678], + [-52.2, 0], + [0, -53.186], + [52.201, 0] + ], + "o": [ + [0, -53.679], + [52.694, 0], + [0, 53.678], + [-52.2, 0] + ], + "v": [ + [448.138, 152.17], + [533.333, 63.527], + [619.021, 152.17], + [533.333, 240.813] + ], + "c": true + }, + "ix": 2 + }, + "nm": "Path 1", + "mn": "ADBE Vector Shape - Group", + "hd": false + }, + { + "ind": 1, + "ty": "sh", + "ix": 2, + "ks": { + "a": 0, + "k": { + "i": [ + [0, -33.487], + [-24.622, 0], + [0, 32.995], + [24.623, 0] + ], + "o": [ + [0, 32.995], + [24.623, 0], + [0, -33.98], + [-24.622, 0] + ], + "v": [ + [495.906, 152.17], + [533.333, 205.848], + [571.253, 152.17], + [533.333, 98.492] + ], + "c": true + }, + "ix": 2 + }, + "nm": "Path 2", + "mn": "ADBE Vector Shape - Group", + "hd": false + }, + { + "ind": 2, + "ty": "sh", + "ix": 3, + "ks": { + "a": 0, + "k": { + "i": [ + [0, 53.186], + [-49.246, 0], + [-0.492, -38.904], + [0, 0], + [16.743, 0], + [0, -32.009], + [-20.684, 0], + [-0.492, 18.714], + [0, 0], + [45.306, 0] + ], + "o": [ + [0, -53.679], + [47.768, 0], + [0, 0], + [-1.97, -19.698], + [-21.176, 0], + [0, 32.01], + [17.236, 0], + [0, 0], + [0, 38.904], + [-49.246, 0] + ], + "v": [ + [270.852, 152.17], + [352.109, 63.527], + [428.44, 129.024], + [381.164, 129.024], + [352.109, 97.999], + [318.128, 151.185], + [352.109, 205.848], + [380.671, 174.823], + [428.44, 174.823], + [352.109, 240.813] + ], + "c": true + }, + "ix": 2 + }, + "nm": "Path 3", + "mn": "ADBE Vector Shape - Group", + "hd": false + }, + { + "ind": 3, + "ty": "sh", + "ix": 4, + "ks": { + "a": 0, + "k": { + "i": [ + [0, 54.663], + [-51.215, 0], + [0, -56.14], + [0.985, -4.432], + [0, 0], + [-27.086, 0], + [-4.925, 13.296], + [0, 0], + [42.845, 0] + ], + "o": [ + [0, -50.231], + [51.709, 0], + [0, 1.477], + [0, 0], + [0, 24.623], + [15.759, 0], + [0, 0], + [-9.85, 36.442], + [-55.648, 0] + ], + "v": [ + [929.763, 150.692], + [1012.989, 63.527], + [1091.782, 155.125], + [1090.797, 164.481], + [976.055, 164.481], + [1012.989, 207.818], + [1042.537, 185.657], + [1090.797, 185.657], + [1013.481, 240.32] + ], + "c": true + }, + "ix": 2 + }, + "nm": "Path 4", + "mn": "ADBE Vector Shape - Group", + "hd": false + }, + { + "ind": 4, + "ty": "sh", + "ix": 5, + "ks": { + "a": 0, + "k": { + "i": [ + [0.985, -24.131], + [0, 0], + [18.714, 0] + ], + "o": [ + [0, 0], + [-0.985, -24.131], + [-18.714, 0] + ], + "v": [ + [974.577, 133.456], + [1045.491, 133.456], + [1010.526, 95.537] + ], + "c": true + }, + "ix": 2 + }, + "nm": "Path 5", + "mn": "ADBE Vector Shape - Group", + "hd": false + }, + { + "ind": 5, + "ty": "sh", + "ix": 6, + "ks": { + "a": 0, + "k": { + "i": [ + [0, 0], + [0, 0], + [17.236, 0], + [0, -36.934], + [0, 0], + [0, 0], + [0, 0], + [16.743, 0], + [0, -36.934], + [0, 0], + [0, 0], + [0, 0], + [0, 0], + [0, 0], + [-22.161, 0], + [-9.849, -19.206], + [-24.13, 0], + [-0.492, -41.367], + [0, 0] + ], + "o": [ + [0, 0], + [0, -21.668], + [-21.668, 0], + [0, 0], + [0, 0], + [0, 0], + [0, -21.668], + [-22.161, 0], + [0, 0], + [0, 0], + [0, 0], + [0, 0], + [0, 0], + [10.342, -16.251], + [23.638, 0], + [11.327, -18.714], + [42.845, 0], + [0, 0], + [0, 0] + ], + "v": [ + [855.401, 236.38], + [855.401, 133.949], + [830.779, 99.969], + [798.769, 153.647], + [798.769, 236.38], + [752.971, 236.38], + [752.971, 133.949], + [727.855, 99.969], + [695.845, 153.647], + [695.845, 235.888], + [650.047, 235.888], + [650.047, 67.959], + [690.428, 67.959], + [689.936, 91.598], + [742.629, 65.497], + [790.889, 91.598], + [846.537, 65.497], + [901.201, 133.949], + [901.201, 236.38] + ], + "c": true + }, + "ix": 2 + }, + "nm": "Path 6", + "mn": "ADBE Vector Shape - Group", + "hd": false + }, + { + "ind": 6, + "ty": "sh", + "ix": 7, + "ks": { + "a": 0, + "k": { + "i": [ + [0, 0], + [0, 0], + [22.653, 0], + [0, -38.904], + [0, 0], + [0, 0], + [0, 0], + [0, 0], + [0, 0], + [-21.175, 0], + [0.492, -43.336], + [0, 0] + ], + "o": [ + [0, 0], + [0, -24.622], + [-21.176, 0], + [0, 0], + [0, 0], + [0, 0], + [0, 0], + [0, 0], + [12.312, -15.759], + [47.768, 0], + [0, 0], + [0, 0] + ], + "v": [ + [196.492, 236.38], + [196.492, 143.305], + [169.406, 99.969], + [136.411, 153.155], + [136.411, 236.38], + [90.613, 236.38], + [90.613, 68.452], + [133.456, 68.452], + [133.456, 90.613], + [186.642, 65.497], + [242.29, 142.813], + [242.29, 236.38] + ], + "c": true + }, + "ix": 2 + }, + "nm": "Path 7", + "mn": "ADBE Vector Shape - Group", + "hd": false + }, + { + "ind": 7, + "ty": "sh", + "ix": 8, + "ks": { + "a": 0, + "k": { + "i": [ + [0, 0], + [0, 0], + [0, 0], + [0, 0] + ], + "o": [ + [0, 0], + [0, 0], + [0, 0], + [0, 0] + ], + "v": [ + [0, 235.888], + [0, 0], + [47.768, 0], + [47.768, 235.888] + ], + "c": true + }, + "ix": 2 + }, + "nm": "Path 8", + "mn": "ADBE Vector Shape - Group", + "hd": false + }, + { + "ty": "fl", + "c": { "a": 0, "k": [0.980392156863, 0.78431372549, 0, 1], "ix": 4 }, + "o": { "a": 0, "k": 100, "ix": 5 }, + "r": 1, + "bm": 0, + "nm": "Fill 1", + "mn": "ADBE Vector Graphic - Fill", + "hd": false + }, + { + "ty": "tr", + "p": { "a": 0, "k": [0, 0], "ix": 2 }, + "a": { "a": 0, "k": [0, 0], "ix": 1 }, + "s": { "a": 0, "k": [100, 100], "ix": 3 }, + "r": { "a": 0, "k": 0, "ix": 6 }, + "o": { "a": 0, "k": 100, "ix": 7 }, + "sk": { "a": 0, "k": 0, "ix": 4 }, + "sa": { "a": 0, "k": 0, "ix": 5 }, + "nm": "Transformieren" + } + ], + "nm": "Vereinigungsmenge 2", + "np": 10, + "cix": 2, + "bm": 0, + "ix": 1, + "mn": "ADBE Vector Group", + "hd": false + } + ], + "ip": 0, + "op": 3641, + "st": 41, + "bm": 0 + } + ], + "markers": [] +}