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 10 commits into
base: main
Choose a base branch
from
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 { WebsiteLanguage } from '@/i18n';
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 }: { lang: WebsiteLanguage }) {
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,60 @@
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"
/>
</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,61 @@
'use client';
Copy link
Contributor

Choose a reason for hiding this comment

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

Is there any particular reason you are making this a client-side component?
I think we can instead make techcard.tsx into a client-side component and store it in a separate folder (components) instead of (sections)

Choose a reason for hiding this comment

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

I encountered issues with client/server components and async/await when adding the tabs. I was able to fix it by adding 'use client' (and replacing Translator.getInstance with useTranslator), but I don't know React well enough to tell if this is the correct fix. I saw that 'use client' is used quite often throughout the code and it seems to do the trick.

When turning techcard.tsx into a client-side component, I think (again, limited React knowledge on my side) I would also need to do the same with techlist.tsx because I am using useState to keep track of the tab state to filter the list and useState only works for client components, correct?

Should I still try to refactor both files into components? And to improve my understanding of React, what are the reasons for not using 'use client'?

Copy link
Contributor

Choose a reason for hiding this comment

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

Hi @lvonlanthen, I completely relate with you. Even I had some difficulty in understanding the difference between client-side and server-side components. Michael had shared a link with me which really helped me clarify my doubts. I shall do the same: NextJS Components.

The main idea here is: Instead of sending the entire component over from the server, just send the data and load the component using browser's resources (i.e. client-side). It is just a good way to reduce server load

That is why I believe that techcard can be a client-side component and techlist can do the data fetching part.
Let me also share one of the pages I worked on recently for you to understand the implementation there.

Here's the server-side component: Section-1 Cards List
and here's the client-side component: Section Card

You can also go through other sections (2 and 3) and the web page so that the point becomes crystal clear.

Finally, feel free to ping me on Slack as well if you would like more discussion around this. Always happy to help 😊.


import TechCard from '@/app/[lang]/[region]/(website)/techstack/(sections)/techcard';
import { SpinnerIcon } from '@/components/logos/spinner-icon';
import { useTranslator } from '@/hooks/useTranslator';
import { WebsiteLanguage } from '@/i18n';
import { Tabs, TabsList, TabsTrigger } from '@socialincome/ui';
import { useState } from 'react';

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

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

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

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

if (!translator || !techArray) {
return (
<div className="mx-auto max-w-6xl">
<div className="flex justify-center pb-10">
<SpinnerIcon />
</div>
</div>
);
}

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>
</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 } }: DefaultPageProps) {
return (
<div className="space-y-24">
<Hero lang={lang} />
<TechList lang={lang} />
</div>
);
}
Loading