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

Add contribute page #479 #637

Merged
merged 25 commits into from
Nov 18, 2023
Merged
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
46773d4
text with translations and a couple images
NatalieWilson19 Sep 27, 2023
309f050
fix links
NatalieWilson19 Sep 27, 2023
39eb6b9
Add link and styling to map image add social media buttons
NatalieWilson19 Sep 27, 2023
5be29d7
add new contribution page to router and footer
NatalieWilson19 Sep 27, 2023
a6ded3e
get rid of redundant divs
NatalieWilson19 Sep 28, 2023
88fc6d0
fix formatting of start a loop
NatalieWilson19 Sep 28, 2023
ce12446
Add 'Where to Find Us' container
NatalieWilson19 Sep 29, 2023
33b9962
fix margins
NatalieWilson19 Sep 29, 2023
d812829
fix background of social media buttons
NatalieWilson19 Sep 29, 2023
366fc17
add mobile formatting
NatalieWilson19 Sep 30, 2023
7980e8e
Add images for contributing to the website
NatalieWilson19 Sep 30, 2023
ce033ed
better styling and text for events
NatalieWilson19 Sep 30, 2023
9a93125
better formatting for website contribution and donations
NatalieWilson19 Sep 30, 2023
79d5ddc
final touches
NatalieWilson19 Oct 1, 2023
5c052f0
Made image for events a link to nearest upcoming event
NatalieWilson19 Oct 4, 2023
3a5b3c3
Refactor
NatalieWilson19 Oct 4, 2023
ee2a875
temporarily add images and delete unused image variables
NatalieWilson19 Oct 9, 2023
7981c05
change order, fix title font, and use smaller font on mobile
NatalieWilson19 Nov 11, 2023
9f4bed6
Add video, fix title font
NatalieWilson19 Nov 12, 2023
903b498
prettier
NatalieWilson19 Nov 13, 2023
2b6845f
Merge branch 'add-contribute-page-#479' of https://github.com/Natalie…
NatalieWilson19 Nov 13, 2023
b59a39b
Use images from the server
NatalieWilson19 Nov 13, 2023
52c3d1f
get rid of where to find us block
NatalieWilson19 Nov 14, 2023
38fdf78
Merge branch 'main' into add-contribute-page-#479
lil5 Nov 17, 2023
2ed038b
Tweaks
lil5 Nov 18, 2023
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
18 changes: 18 additions & 0 deletions frontend/public/locales/en/contribute.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"howToContribute": "Contributing to the Clothing Loop",
"startALoop": "Start a Loop!",
"startALoopDesc": "Consider starting a loop as a host. The Clothing Loop is <strong>free to use and a great way to connect with people</strong>. If you want to join a loop and there isn't one already in your area, don't be afraid to start one!",
"shareSwapStory": "Share your Clothing Loop Swapping Story",
"shareSwapStoryDesc": "If you've had a positive experience with the Clothing Loop and want to spread the word, tag us on social media. We are on <aInstagram>Instagram</aInstagram>, <aFacebook>Facebook</aFacebook>, and <aLinkedin>LinkedIn</aLinkedin>. <strong>We would love to hear how the Clothing Loop has impacted you!</strong>",
"crowdin": "Help us translate with <aCrowdin>Crowdin</aCrowdin>",
"crowdinDesc": "Our web app is able to reach you in <strong>eight different languages</strong> due to the kind people who take the time to <strong>translate it using <aCrowdin>Crowdin</aCrowdin></strong>. We would love for the Clothing Loop to be accessible to as many people as possible, and if you're multilingual and want to contribute to the Clothing Loop, consider adding translations on Crowdin.",
"swap": "Plan a Clothes Swap Event!",
"swapDesc": "A swap party is a great way to trade clothes and get the chance to meet up with friends and neighbors and swap clothes! Also, <strong>check out <aEvents>upcoming events</aEvents> on our calendar.</strong>",
"feedback": "Send us Feedback",
"feedbackDesc": "We are always excited to hear how the Clothing Loop has impacted our members or how we could improve. If you have <strong>improvement suggestions</strong> or want to <strong>share how the Clothing Loop has affected you</strong>, please don’t hesitate to share your thoughts with us!",
"website": "Contribute to Our Website",
"websiteDesc": "<strong>Do you code?</strong> The Clothing Loop is an <strong>open-source website</strong>. Click <aGithub>here</aGithub> to see our open tasks on Github or <aEmail>email us</aEmail> if you're interested in volunteering your programming skills and want to connect with us on Slack and join our weekly developer meetings.",
"donate": "Donate",
"donateDesc": "All <aDonate>donations</aDonate> to the Clothing Loop are greatly appreciated to help offset costs to keep the Clothing Loop <strong>free and available for all users!</strong>",
"WhereToFindUs": "Where to Find Us"
}
7 changes: 6 additions & 1 deletion frontend/src/App.tsx
Original file line number Diff line number Diff line change
@@ -25,6 +25,7 @@ import Home from "./pages/Home";
import { ToastProvider } from "./providers/ToastProvider";
import { useTranslation } from "react-i18next";
import { t } from "i18next";
import Contribute from "./pages/Contribute";

// Lazy
const FindChain = React.lazy(() => import("./pages/FindChain"));
@@ -207,7 +208,11 @@ export default function App() {
path={`${base}/privacy-policy`}
component={PrivacyPolicy}
/>

<Route
exact
path={`${base}/contribute`}
component={Contribute}
/>
<Route
exact
path={`${base}/admin/dashboard`}
8 changes: 3 additions & 5 deletions frontend/src/components/Footer.tsx
Original file line number Diff line number Diff line change
@@ -209,14 +209,12 @@ export default function Footer() {
>
{t("privacy")}
</Link>
<a
<Link
className="btn btn-ghost text-white text-base font-normal"
href="https://github.com/the-clothing-loop/website"
target="_blank"
rel="noreferrer"
to="/contribute"
>
{t("contribute")}
</a>
</Link>
</div>

<p className="text-center sm:text-right" aria-label="copyright">
422 changes: 422 additions & 0 deletions frontend/src/pages/Contribute.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,422 @@
import { useContext, useEffect, useState } from "react";
import { Helmet } from "react-helmet";

import { Trans, useTranslation } from "react-i18next";
import { Link } from "react-router-dom";
import { Event } from "../api/types";
import { eventGetAll } from "../api/event";
import { GinParseErrors } from "../util/gin-errors";
import { ToastContext } from "../providers/ToastProvider";
import dayjs from "../util/dayjs";
import { SizeBadges } from "../components/Badges";
import { getLanguageFlags } from "../languages";

const translationFlags = getLanguageFlags(false);

// Media
const CirclesFrame = "https://images.clothingloop.org/0x0/circles.png";
const map = "https://images.clothingloop.org/640x0/map_image_4.png";
const ClothesImage =
"https://images.clothingloop.org/768x/nichon_zelfportret.jpg";

export default function Contribute() {
const { t } = useTranslation("contribute");
const { addToastError } = useContext(ToastContext);
const [event, setEvent] = useState<Event | null>(null);
const [isHoveringDonate, setIsHoveringDonate] = useState(false);

useEffect(() => {
window.goatcounter?.count({
path: "accessed-page-contribute",
title: "Accessed Page:Contribute",
event: true,
});
loadNextUpcomingEvent();
}, []);

async function loadNextUpcomingEvent() {
try {
const latitude = 52.377956;
const longitude = 4.89707;
const radius = 3000;

const _events = await eventGetAll({ latitude, longitude, radius });
setEvent(
_events.data.sort((a, b) =>
new Date(a.date) > new Date(b.date) ? 1 : -1
)[0]
);
} catch (err: any) {
addToastError(GinParseErrors(t, err), err.status);
}
}

function NextUpcomingEvent({ event }: { event: Event }) {
const { t } = useTranslation();
const date = dayjs(event.date);

const eventPriceValue =
event.price_value % 1 === 0
? event.price_value
: event.price_value.toFixed(2);

let image = ClothesImage;
if (event.image_url) image = event.image_url;
return (
<article className="flex flex-col bg-teal-light">
<Link
to="/events"
className="relative aspect-[4/3] overflow-hidden"
target="_blank"
>
<div className=" text-md absolute mt-4 right-4 text-center z-10">
<p className="bg-teal text-white py-2 px-3">
<span className="inline-block pr-1 font-extrabold">
{date.format("MMMM")}
</span>
<span>{" " + date.format("D")}</span>
</p>
{event.price_currency ? (
<p className="py-1 px-3 bg-yellow-dark text-black">
<span className="inline-block pr-1 font-bold">
{event.price_currency}
</span>
<span className="inline-block pr-1 font-bold">
{eventPriceValue}
</span>
</p>
) : (
<p className="py-1 px-3 bg-white/90 text-black">
<span className="inline-block pr-1 font-semibold">
{t("priceFree")}
</span>
</p>
)}
</div>
<img src={image} className="w-full h-full object-cover" />
</Link>

<div className="m-4 mb-2">
<h2 className="text-xl text-teal font-bold">
<Link to={"/events/" + event.uid}>{event.name}</Link>
</h2>
</div>
<div className="flex-grow mx-4 mb-2">
<span className="feather feather-map-pin mr-2 rtl:mr-0 rtl:ml-2"></span>
<address className="inline">{event.address}</address>
</div>
<div className="m-4 mt-0">
{event.genders?.length ? <SizeBadges g={event.genders} /> : null}
</div>
</article>
);
}

async function videoHandler(play: boolean) {
let video = document.getElementById("github-video") as HTMLVideoElement;
try {
if (play) {
await video.play();
} else {
await video.pause();
}
} catch (err) {
console.log(err);
}
}

return (
<>
<Helmet>
<title>The Clothing Loop | How to Contribute</title>
<meta
name="description"
content="How to Contribute to the Clothing Loop"
/>
</Helmet>
<main>
<div className="max-w-screen-xl mx-auto pt-10 px-10 md:px-20">
<h1 className="font-serif font-bold text-secondary text-4xl md:text-6xl mb-16">
{t("howToContribute")}
</h1>
<div className="flex flex-col md:flex-row items-center mb-8">
<div className="w-full md:w-1/2">
<h2 className="-mt-8 mb-4">
<Link
className="text-xl md:text-2xl font-bold text-secondary group"
to="/donate"
>
{t("donate")}
<span
className={`ms-2 align-text-bottom border rounded-full inline-flex items-center justify-center w-8 h-8 group-hover:bg-red-light group-hover:text-white transition-colors ${
isHoveringDonate ? "bg-red-light text-white" : "text-red"
}`}
>
<span className="feather feather-heart text-base font-bold" />
</span>
</Link>
</h2>
<p className="prose font-normal mb-8">
<Trans
i18nKey="donateDesc"
ns="contribute"
components={{
aDonate: (
<Link
className="link"
to="/donate"
target="_blank"
onMouseEnter={() => setIsHoveringDonate(true)}
onMouseLeave={() => setIsHoveringDonate(false)}
></Link>
),
}}
/>
</p>
<h2 className="text-xl md:text-2xl font-bold text-secondary mb-4">
<Trans i18nKey="startALoop" ns="contribute" />
</h2>
<p className="prose mb-4">
<Trans i18nKey="startALoopDesc" ns="contribute" />
</p>
<Link
to="/"
className="btn btn-primary w-full sm:w-auto btn-outline text-black md:mb-8"
>
{t("startNewLoop", { ns: "translation" })}
</Link>
</div>
<Link
className="hidden md:block md:w-1/2 md:pl-12"
to="/loops/find"
target="_blank"
>
<img
src={map}
alt="map of the clothing loop in Amsterdam area"
className="object-cover hover:ring-[1.5rem] ring-secondary transition-[box-shadow]"
/>
</Link>
</div>

<div className="flex flex-col md:flex-row items-center mb-8 md:mb-16">
<div className="relative w-full md:w-1/3 mb-8 md:mb-0">
<div className="object-cover hover:ring-[1.5rem] ring-secondary transition-[box-shadow]">
{event ? (
<NextUpcomingEvent event={event} key={event.uid} />
) : (
<Link
to="/events"
className="relative aspect-[4/3] overflow-hidden"
target="_blank"
>
<img
src={ClothesImage}
className="w-full h-full object-cover"
/>
</Link>
)}
</div>
<img
className="hidden md:block -z-10 absolute -right-10 -top-10"
src={CirclesFrame}
aria-hidden
alt=""
/>
<img
className="hidden md:block -z-10 absolute -left-10 -bottom-10"
aria-hidden
alt=""
src={CirclesFrame}
/>
</div>

<div className="w-full md:w-2/3 md:pl-24 ">
<div className="w-full md:w-2/3 mb-8 md:mb-0">
<h2 className="text-xl md:text-2xl font-bold text-secondary mb-4">
<Trans i18nKey="swap" ns="contribute" />
</h2>
<p className="prose font-normal mb-8">
<Trans
i18nKey="swapDesc"
ns="contribute"
components={{
aEvents: (
<Link
className="link font-bold"
to="/events"
target="_blank"
></Link>
),
}}
/>
</p>
</div>
<h2 className="text-xl md:text-2xl font-bold text-secondary mb-4">
<Trans i18nKey="shareSwapStory" ns="contribute" />
</h2>
<p className="prose font-normal mb-8">
<Trans
i18nKey="shareSwapStoryDesc"
ns="contribute"
components={{
p: <p></p>,
aInstagram: (
<a
className="link"
href="https://www.instagram.com/theclothingloop/"
target="_blank"
/>
),
aFacebook: (
<a
className="link"
href="https://www.facebook.com/clothingloop/"
target="_blank"
/>
),
aLinkedin: (
<a
className="link"
href="https://www.linkedin.com/company/the-clothing-loop/"
target="_blank"
/>
),
}}
/>
</p>
<h2 className="text-xl md:text-2xl font-bold text-secondary mb-4">
<Trans i18nKey="website" ns="contribute" />
</h2>
<p className="prose font-normal mb-3">
<Trans
i18nKey="websiteDesc"
ns="contribute"
components={{
p: <p></p>,
aGithub: (
<a
className="link"
href="https://github.com/the-clothing-loop/website/issues"
target="_blank"
/>
),
aEmail: (
<a
className="link"
href="mailto:hello@clothingloop.org"
aria-label="Our email address"
></a>
),
}}
/>
</p>
<div className="flex gap-4">
<a
className="group"
target="_blank"
href="https://github.com/the-clothing-loop/website/issues"
>
<span className="flex border rounded-full h-12 px-4 items-center group-hover:text-white group-hover:border-base-content group-hover:bg-black text-lg transition-colors">
<span className="feather feather-git-branch me-2" />
Github
</span>
</a>
<a
className="group"
href="mailto:hello@clothingloop.org"
aria-label="Our email address"
>
<span className="btn btn-circle btn-outline flex justify-center group-hover:text-white group-hover:border-base-content group-hover:bg-[#b464a8] feather feather-mail text-lg"></span>
</a>
</div>
</div>
</div>
<div className="flex flex-col-reverse md:flex-row mb-8">
<div className="w-full md:w-1/2 md:pr-4">
<p className="text-xl md:text-2xl font-bold text-secondary mb-4">
<Trans
i18nKey="crowdin"
ns="contribute"
components={{
aCrowdin: (
<a
className="underline"
href="https://crowdin.com/project/the-clothing-loop"
target="_blank"
/>
),
}}
/>
</p>
<p className="prose font-normal mb-2">
<Trans
i18nKey="crowdinDesc"
ns="contribute"
components={{
aCrowdin: (
<a
className="link font-bold"
href="https://crowdin.com/project/the-clothing-loop"
target="_blank"
/>
),
}}
/>
</p>
<ol className="flex flex-wrap gap-2 mb-8">
{translationFlags.map((flag) => {
const isEN = flag.lng === "en";
let crowdinUrl =
"https://crowdin.com/project/the-clothing-loop/";
if (flag.lng !== "en") {
crowdinUrl += flag.lng;
}
return (
<li>
<a href={crowdinUrl} target="_blank">
<img
src={flag.flag}
alt={flag.lng}
className="w-10 border-4 border-transparent hover:border-teal/40 transition-colors"
/>
</a>
</li>
);
})}
</ol>
<h2 className="text-xl md:text-2xl font-bold text-secondary mb-4">
<Trans i18nKey="feedback" ns="contribute" />
</h2>
<p className="prose font-normal mb-4">
<Trans i18nKey="feedbackDesc" ns="contribute" />
</p>
<Link to="/contact-us" className="btn btn-secondary btn-outline">
{t("contactUs", { ns: "translation" })}
<span className="feather feather-arrow-right ml-4 rtl:hidden" />
<span className="feather feather-arrow-left mr-4 ltr:hidden" />
</Link>
</div>
<div
onMouseEnter={() => videoHandler(true)}
onMouseLeave={() => videoHandler(false)}
className="w-full md:w-1/2 aspect-video md:pl-4 max-xs:-mx-8 max-xs:w-auto self-center mb-8 md:mb-0"
>
<video
title="Editing files as a display of fireworks from our Github repository"
id="github-video"
muted
loop
>
<source
src="https://images.clothingloop.org/original/gource-cut1.webm"
type="video/webm"
/>
</video>
</div>
</div>
</div>
</main>
</>
);
}