Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Website: New page to present tech stack #976

Draft
wants to merge 5 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
148 changes: 148 additions & 0 deletions shared/locales/en/website-techstack.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
{
"metadata": {
"title": "Social Income",
"description": "Our Techstack",
"og-image": "/assets/metadata/og/default.jpg",
"twitter-image": "/assets/metadata/twitter/default.jpg"
},
"hero": {
"title-1": [
{
"text": "To work efficiently and effectively, "
},
{
"text": "we rely on the right tools.",
"color": "accent"
}
],
"subtitle": "At Social Income, donated tech and software play a crucial role in driving our mission forward."
},
"tabs": {
"tech-stack": "Tech Stack",
"donated-tech": "Donated Tech"
},
"badges": {
"donated": "Donated"
},
"cards": [
{
"title": "Google Suite",
"description": "We use the tools to organize and communicate. Thanks to Google Nonprofit, this is free for us.",
"link": "https://www.google.com/nonprofits/",
"logo": "google-nonprofit.png",
"donated": true
},
{
"title": "GitHub",
"description": "We use GitHub to collaborate on code; without it, maintaining an open-source approach would be difficult.",
"link": "https://socialimpact.github.com/",
"logo": "",
"donated": false
},
{
"title": "Codemagic",
"description": "We use Codemagic to simplify app builds and reduce deployment complexity.",
"link": "https://codemagic.io/start/",
"logo": "",
"donated": false
},
{
"title": "Linktree",
"description": "We use Linktree to keep all our social media links in one accessible place.",
"link": "https://linktr.ee/",
"logo": "",
"donated": false
},
{
"title": "Twilio",
"description": "We use Twilio to send newsletters and transactional emails to donors, ensuring reliable communication.",
"link": "https://twilio.org/",
"logo": "",
"donated": false
},
{
"title": "Algolia",
"description": "We use Algolia to enable fast and accurate search within our admin tool.",
"link": "https://www.algolia.com/",
"logo": "",
"donated": false
},
{
"title": "JetBrains",
"description": "We use JetBrains to write code and review open-source contributions efficiently.",
"link": "https://www.jetbrains.com/",
"logo": "",
"donated": false
},
{
"title": "1Password",
"description": "We use 1Password to keep our team’s credentials secure and easy to access when needed.",
"link": "https://1password.com/",
"logo": "",
"donated": false
},
{
"title": "Mux",
"description": "We use Mux to provide fast and adaptable video experiences to website users.",
"link": "https://www.mux.com/",
"logo": "",
"donated": false
},
{
"title": "Sentry",
"description": "We use Sentry to monitor, identify, and fix issues across all our tools, improving reliability.",
"link": "https://sentry.io/",
"logo": "",
"donated": false
},
{
"title": "Unica77",
"description": "We use Unica77, a donated font by Lineto, to give our platform a distinctive and professional look.",
"link": "https://lineto.com/typefaces/unica77",
"logo": "",
"donated": false
},
{
"title": "FireCMS",
"description": "We use FireCMS to manage our Firestore data and run the admin tool seamlessly.",
"link": "https://firecms.co/",
"logo": "",
"donated": false
},
{
"title": "Storybook",
"description": "We use Storybook to maintain a consistent and cohesive design across components.",
"link": "https://storybook.js.org/",
"logo": "",
"donated": false
},
{
"title": "Tailwind CSS",
"description": "We use Tailwind CSS to style quickly and flexibly, adapting to our evolving needs.",
"link": "https://tailwindcss.com/",
"logo": "",
"donated": false
},
{
"title": "Shadcn",
"description": "We use Shadcn components because they offer a great balance of simplicity and versatility.",
"link": "https://ui.shadcn.com/",
"logo": "",
"donated": false
},
{
"title": "TypeScript",
"description": "We use TypeScript to write safer and more maintainable code with fewer bugs.",
"link": "https://www.typescriptlang.org/",
"logo": "",
"donated": false
},
{
"title": "Next.js",
"description": "We use Next.js to deploy our website as fast as possible.",
"link": "https://nextjs.org/",
"logo": "",
"donated": false
}
]
}
Binary file added website/public/assets/tech/google-nonprofit.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -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-techstack'],
});
ssandino marked this conversation as resolved.
Show resolved Hide resolved

return (
<div className="mx-auto mb-8 mt-20 flex w-4/5 flex-col items-center justify-center md:mb-20 lg:w-3/5">
<div className="mb-8 text-center">
{translator.t<{ text: string; color?: FontColor }[]>('hero.title-1').map((title, index) => (
<Typography as="span" size="5xl" weight="medium" color={title.color} key={index}>
{title.text}{' '}
</Typography>
))}
ssandino marked this conversation as resolved.
Show resolved Hide resolved
</div>
<Typography size="2xl" className="mb-8 text-center">
{translator.t('hero.subtitle')}
</Typography>
</div>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { Badge, Card, CardContent, CardHeader, CardTitle, Typography } from '@socialincome/ui';
import Image from 'next/image';
import Link from 'next/link';

type TechCardTranslations = {
badgeDonated: string;
};

type TechCardProps = {
title: string;
description: string;
link: string;
logo: string;
donated: boolean;
translations: TechCardTranslations;
};

function getLogoSrc(logo: string) {
const image_base_path = '/assets/tech/';
return image_base_path.concat(logo);
}
ssandino marked this conversation as resolved.
Show resolved Hide resolved

export default function TechCard({ title, description, link, logo, donated, translations }: TechCardProps) {
return (
<Card className="hover:bg-primary max-w-lg rounded-lg p-6 shadow-none hover:bg-opacity-10">
<Link href={link} target="_blank" rel="noreferrer" className="flex flex-col gap-6 sm:flex-row">
ssandino marked this conversation as resolved.
Show resolved Hide resolved
{!!logo && (
<div className="w-fit basis-1/4 self-center">
<Image
src={getLogoSrc(logo)}
alt={title}
className="mx-auto w-1/2 rounded-sm sm:w-full"
width="48"
height="48"
unoptimized
/>
Comment on lines +29 to +36
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Enable image optimization for better performance.

The unoptimized prop bypasses Next.js's built-in image optimization. Consider removing it to benefit from automatic image optimization.

 <Image
 	src={getLogoSrc(logo)}
 	alt={title}
 	className="mx-auto w-1/2 rounded-sm sm:w-full"
 	width="48"
 	height="48"
-	unoptimized
 />
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<Image
src={getLogoSrc(logo)}
alt={title}
className="mx-auto w-1/2 rounded-sm sm:w-full"
width="48"
height="48"
unoptimized
/>
<Image
src={getLogoSrc(logo)}
alt={title}
className="mx-auto w-1/2 rounded-sm sm:w-full"
width="48"
height="48"
/>

</div>
)}
<div className={'' + (!!logo ? 'w-fit basis-3/4' : '')}>
{donated && (
<Badge className="bg-accent hover:bg-accent text-primary text-md float-right -m-3 border-none">
<Typography size="md" weight="normal" className="p-1">
{translations.badgeDonated}
</Typography>
</Badge>
)}
<CardHeader className="p-0">
<CardTitle className="flex items-center justify-between">
<Typography size="2xl" weight="medium">
{title}
</Typography>
</CardTitle>
</CardHeader>
<CardContent className="my-4 p-0">
<Typography size="lg">{description}</Typography>
</CardContent>
</div>
</Link>
</Card>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
'use client';

import { DefaultParams } from '@/app/[lang]/[region]';
import TechCard from '@/app/[lang]/[region]/(website)/techstack/(sections)/techcard';
import { useTranslator } from '@/hooks/useTranslator';
import { Tabs, TabsList, TabsTrigger } from '@socialincome/ui';
import React from 'react';

type TechEntryJSON = {
title: string;
description: string;
link: string;
logo: string;
donated: boolean;
};

export function TechList({ lang }: DefaultParams) {
const [isDonated, setIsDonated] = React.useState(false);

const translator = useTranslator(lang, 'website-techstack');
const techArray: TechEntryJSON[] | undefined = translator?.t('cards');

const handleTabChange = (value: string) => {
setIsDonated(value === 'donated');
};

return (
<div className="mx-auto max-w-6xl">
<div className="flex justify-center pb-10">
<Tabs defaultValue="tech" className="w-[400px]" onValueChange={handleTabChange}>
<TabsList className="bg-primary grid w-full grid-cols-2 bg-opacity-10">
<TabsTrigger value="tech">{translator?.t('tabs.tech-stack')}</TabsTrigger>
<TabsTrigger value="donated">{translator?.t('tabs.donated-tech')}</TabsTrigger>
</TabsList>
</Tabs>
</div>
<div className="grid grid-cols-1 gap-4 p-4 sm:grid-cols-2 lg:grid-cols-2">
{techArray
?.filter((t) => !isDonated || t.donated)
.map((techEntry, index) => (
<TechCard
{...techEntry}
translations={{ badgeDonated: translator?.t('badges.donated') || '' }}
key={index}
/>
))}
</div>
Comment on lines +38 to +47
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add null checks and loading state handling.

The code assumes techArray and translator are always available, which might not be true during initial render or translation loading.

Add proper checks and loading state:

+ if (!translator || !techArray) {
+   return <div>Loading...</div>;
+ }

  <div className="grid grid-cols-1 gap-4 p-4 sm:grid-cols-2 lg:grid-cols-2">
    {techArray
-     ?.filter((t) => !isDonated || t.donated)
+     .filter((t) => !isDonated || t.donated)
      .map((techEntry, index) => (
        <TechCard
          {...techEntry}
-         translations={{ badgeDonated: translator?.t('badges.donated') || '' }}
+         translations={{ badgeDonated: translator.t('badges.donated') }}
          key={index}
        />
      ))}
  </div>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
{techArray
?.filter((t) => !isDonated || t.donated)
.map((techEntry, index) => (
<TechCard
{...techEntry}
translations={{ badgeDonated: translator?.t('badges.donated') || '' }}
key={index}
/>
))}
</div>
if (!translator || !techArray) {
return <div>Loading...</div>;
}
<div className="grid grid-cols-1 gap-4 p-4 sm:grid-cols-2 lg:grid-cols-2">
{techArray
.filter((t) => !isDonated || t.donated)
.map((techEntry, index) => (
<TechCard
{...techEntry}
translations={{ badgeDonated: translator.t('badges.donated') }}
key={index}
/>
))}
</div>

</div>
);
}
12 changes: 12 additions & 0 deletions website/src/app/[lang]/[region]/(website)/techstack/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { DefaultPageProps } from '@/app/[lang]/[region]';
import { Hero } from './(sections)/hero';
import { TechList } from './(sections)/techlist';

export default async function Page({ params: { lang, region } }: DefaultPageProps) {
return (
<div className="space-y-24">
<Hero lang={lang} region={region} />
<TechList lang={lang} region={region} />
</div>
);
}
Loading