From 85e6be25f590c960a5f496b2306c0be38f4a48b2 Mon Sep 17 00:00:00 2001
From: aliraza556
Date: Tue, 31 Dec 2024 20:17:38 +0500
Subject: [PATCH 1/2] feat: add bounty steps and featured sections to landing
page
---
.../BountyComponents/BountySteps.tsx | 70 ++++++++++++++++++
.../BountyComponents/FeaturedBounties.tsx | 74 +++++++++++++++++++
src/components/BountyComponents/index.tsx | 10 +++
src/pages/BountiesLandingPage/index.tsx | 2 +
4 files changed, 156 insertions(+)
create mode 100644 src/components/BountyComponents/BountySteps.tsx
create mode 100644 src/components/BountyComponents/FeaturedBounties.tsx
create mode 100644 src/components/BountyComponents/index.tsx
diff --git a/src/components/BountyComponents/BountySteps.tsx b/src/components/BountyComponents/BountySteps.tsx
new file mode 100644
index 00000000..08dcafad
--- /dev/null
+++ b/src/components/BountyComponents/BountySteps.tsx
@@ -0,0 +1,70 @@
+import React from 'react';
+import styled from 'styled-components';
+import { colors } from '../../config/colors';
+
+const StepsContainer = styled.div`
+ display: flex;
+ flex-direction: column;
+ gap: 2px;
+ margin: 20px 0;
+`;
+
+const StepItem = styled.div`
+ display: flex;
+ align-items: flex-start;
+ gap: 4px;
+ font-size: 16px;
+ margin-left: 2%;
+ color: ${colors.light.text1};
+`;
+
+const StepLabel = styled.span`
+ font-weight: 800;
+ font-size: 18px;
+`;
+
+const StepText = styled.span`
+ font-weight: 500;
+`;
+
+const StepTitle = styled.h6`
+ font-size: 20px;
+ font-family: 'Barlow';
+ font-weight: 800;
+ color: ${colors.light.text1};
+ margin-bottom: 20px;
+`;
+
+interface Step {
+ text: string;
+}
+
+const steps: Step[] = [
+ {
+ text: 'Sign up for a Sphinx by clicking the Get Sphinx button!'
+ },
+ {
+ text: 'Check out the open bounties, by clicking bounties!'
+ },
+ {
+ text: 'Reach out to the bounty provider by clicking "I can help!"'
+ },
+ {
+ text: 'Introduce yourself and get assigned'
+ },
+ {
+ text: 'Compelte the work and get paid directly to your Sphinx Wallet!'
+ }
+];
+
+export const BountySteps: React.FC = () => (
+
+ If you want to earn bounties
+ {steps.map((step: Step, index: number) => (
+
+ {`Step ${index + 1}:`}
+ {step.text}
+
+ ))}
+
+);
diff --git a/src/components/BountyComponents/FeaturedBounties.tsx b/src/components/BountyComponents/FeaturedBounties.tsx
new file mode 100644
index 00000000..2fab1a5e
--- /dev/null
+++ b/src/components/BountyComponents/FeaturedBounties.tsx
@@ -0,0 +1,74 @@
+import React from 'react';
+import styled from 'styled-components';
+import { observer } from 'mobx-react-lite';
+import { colors } from '../../config/colors';
+import { bountyStore } from '../../store/bountyStore';
+
+const FeaturedContainer = styled.div`
+ margin: 20px 0;
+`;
+
+const FeaturedHeader = styled.div`
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ margin-bottom: 16px;
+
+ h2 {
+ font-size: 18px;
+ font-weight: 600;
+ color: ${colors.light.text1};
+ margin: 0;
+ }
+`;
+
+const BountyCard = styled.div`
+ padding: 16px;
+ border: 1px solid ${colors.light.grayish.G900};
+ border-radius: 8px;
+ cursor: pointer;
+ transition: all 0.2s ease;
+
+ &:hover {
+ background: ${colors.light.grayish.G950};
+ }
+`;
+
+const BountyTitle = styled.h3`
+ font-size: 16px;
+ font-weight: 500;
+ color: ${colors.light.text1};
+ margin: 0 0 8px 0;
+`;
+
+const BountyMeta = styled.div`
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ color: ${colors.light.text2};
+ font-size: 14px;
+`;
+
+export const FeaturedBounties: React.FC = observer(() => {
+ const featuredBounty = bountyStore.getFeaturedBounty();
+ return (
+
+
+ Featured Bounties
+
+
+ {featuredBounty ? (
+ (window.location.href = featuredBounty.url)}>
+ Bounty #{featuredBounty.bountyId}
+
+ View Details
+
+
+ ) : (
+
+ No featured bounties at the moment
+
+ )}
+
+ );
+});
diff --git a/src/components/BountyComponents/index.tsx b/src/components/BountyComponents/index.tsx
new file mode 100644
index 00000000..be2c6d2d
--- /dev/null
+++ b/src/components/BountyComponents/index.tsx
@@ -0,0 +1,10 @@
+import React from 'react';
+import { BountySteps } from './BountySteps';
+import { FeaturedBounties } from './FeaturedBounties';
+
+export const BountyComponents = () => (
+ <>
+
+
+ >
+);
diff --git a/src/pages/BountiesLandingPage/index.tsx b/src/pages/BountiesLandingPage/index.tsx
index d7dbcaac..2205fabe 100644
--- a/src/pages/BountiesLandingPage/index.tsx
+++ b/src/pages/BountiesLandingPage/index.tsx
@@ -6,6 +6,7 @@ import { colors } from '../../config/colors';
import { BountiesHeader, HeaderWrap, Leftheader } from '../tickets/style.ts';
import { BountyHeaderContent } from '../tickets/workspace/workspaceHeader/WorkspaceHeaderStyles.tsx';
import TopEarners from '../../components/common/TopEarners/index.tsx';
+import { BountyComponents } from '../../components/BountyComponents';
const BountiesLandingPage: React.FC = () => {
const isMobile = useIsMobile();
@@ -103,6 +104,7 @@ const BountiesLandingPage: React.FC = () => {
Building the modern marketplace for work. Complete a bounty and get paid in Bitcoin
instantly! ⚡
+
Freedom to Earn!
From 4da7dc23bc2011bf204f9ad262d479edcd71ae57 Mon Sep 17 00:00:00 2001
From: aliraza556
Date: Wed, 1 Jan 2025 03:06:28 +0500
Subject: [PATCH 2/2] Implement Featured Bounties display
---
src/bounties/BountyDescription.tsx | 35 +++++--
src/bounties/BountyPrice.tsx | 3 +
src/bounties/BountyProfileView.tsx | 11 ++-
src/bounties/interfaces.ts | 3 +
.../BountyComponents/FeaturedBounties.tsx | 94 +++++++++++--------
src/pages/BountiesLandingPage/index.tsx | 6 +-
src/people/interfaces.ts | 3 +
.../utils/AssignedUnassignedBounties.tsx | 10 +-
src/people/utils/NameTag.tsx | 7 +-
src/people/widgetViews/WantedView.tsx | 4 +-
src/people/widgetViews/WidgetSwitchViewer.tsx | 6 +-
11 files changed, 124 insertions(+), 58 deletions(-)
diff --git a/src/bounties/BountyDescription.tsx b/src/bounties/BountyDescription.tsx
index b9516e02..4980860a 100644
--- a/src/bounties/BountyDescription.tsx
+++ b/src/bounties/BountyDescription.tsx
@@ -19,27 +19,35 @@ interface codingLangProps {
interface bounty_description_props {
isPaid?: any;
color?: any;
+ isBountyLandingPage?: boolean;
}
const BountyDescriptionContainer = styled.div`
display: flex;
flex-direction: column;
height: 100%;
- min-width: 519px;
- max-width: 519px;
+ min-width: ${(props: any) => (props.isBountyLandingPage ? '270px' : '519px')};
+ max-width: ${(props: any) => (props.isBountyLandingPage ? '270px' : '519px')};
padding-left: 17px;
padding-right: 17px;
`;
-const Header = styled.div`
+interface bounty_header_props {
+ isBountyLandingPage?: boolean;
+}
+
+const Header = styled.div`
display: flex;
flex-direction: row;
justify-content: space-between;
height: 32px;
margin-top: 16px;
+ margin-left: 0;
+
.NameContainer {
display: flex;
flex-direction: column;
+ width: ${(props: any) => (props.isBountyLandingPage ? '320px' : '')};
}
`;
@@ -75,11 +83,16 @@ const Description = styled.div`
}
`;
-const LanguageContainer = styled.div`
+interface LanguageContainerProps {
+ isBountyLandingPage?: boolean;
+}
+
+const LanguageContainer = styled.div`
display: flex;
flex-wrap: wrap;
width: 80%;
margin-top: 10px;
+ margin-left: ${(p: any) => (p.isBountyLandingPage ? '95px' : '0px')};
`;
const CodingLabels = styled.div`
@@ -121,6 +134,8 @@ const BountyDescription = (props: BountiesDescriptionProps) => {
const [replitLink, setReplitLink] = useState('');
const [descriptionImage, setDescriptionImage] = useState('');
+ const { isBountyLandingPage } = props;
+
useEffect(() => {
if (props.description) {
const found = props?.description.match(/(https?:\/\/.*\.(?:png|jpg|jpeg|gif))(?![^`]*`)/);
@@ -147,8 +162,11 @@ const BountyDescription = (props: BountiesDescriptionProps) => {
return (
<>
-
-
+
+
{
{props.title?.slice(0, descriptionImage ? 80 : 120)}
@@ -208,7 +227,7 @@ const BountyDescription = (props: BountiesDescriptionProps) => {
-
+
{replitLink && (
window.open(replitLink[0])} style={{ display: 'flex' }}>
`
@@ -18,6 +19,7 @@ const PriceContainer = styled.div`
align-items: flex-start;
padding: 0px 24px;
color: #909baa;
+ margin-left: ${(props: any) => (props.isBountyLandingPage ? '200px' : '0')};
padding-top: 41px;
.PriceStaticTextContainer {
width: 28px;
@@ -101,6 +103,7 @@ const BountyPrice = (props: BountiesPriceProps) => {
style={{
...props.style
}}
+ isBountyLandingPage={props.isBountyLandingPage}
>
`
min-width: 336px;
max-width: 336px;
display: flex;
- padding: 40px 0px 0px 37px;
+ padding: ${(props: any) =>
+ props.isBountyLandingPage ? '40px 0px 0px 20px' : '40px 0px 0px 37px'};
+ margin-left: ${(props: any) => (props.isBountyLandingPage ? '-10px' : '0')};
`;
const UserImage = styled.div`
@@ -127,6 +133,7 @@ const BountyProfileView = (props: BountiesProfileProps) => {
style={{
...props?.UserProfileContainerStyle
}}
+ isBountyLandingPage={props.isBountyLandingPage}
>
{
+ const featuredBounty = bountyStore.getFeaturedBounty();
+ const [currentItems, setCurrentItems] = useState(1);
+ const [page, setPage] = useState(1);
- &:hover {
- background: ${colors.light.grayish.G950};
- }
-`;
+ const widgetViewerProps = {
+ onPanelClick: (activeWorkspace?: string, bounty?: any) => {
+ if (bounty?.id) {
+ window.location.href = featuredBounty?.url || '';
+ }
+ },
+ checkboxIdToSelectedMap: {},
+ checkboxIdToSelectedMapLanguage: {},
+ fromBountyPage: true,
+ selectedWidget: 'bounties',
+ isBountyLandingPage: true,
+ loading: false,
+ currentItems,
+ setCurrentItems: (items: number) => setCurrentItems(items),
+ page,
+ setPage: (newPage: number) => setPage(newPage),
+ languageString: '',
-const BountyTitle = styled.h3`
- font-size: 16px;
- font-weight: 500;
- color: ${colors.light.text1};
- margin: 0 0 8px 0;
-`;
+ bounties: featuredBounty
+ ? [
+ {
+ body: {
+ id: featuredBounty.bountyId
+ },
+ person: {
+ wanteds: []
+ }
+ }
+ ]
+ : []
+ };
-const BountyMeta = styled.div`
- display: flex;
- align-items: center;
- justify-content: space-between;
- color: ${colors.light.text2};
- font-size: 14px;
-`;
-
-export const FeaturedBounties: React.FC = observer(() => {
- const featuredBounty = bountyStore.getFeaturedBounty();
return (
Featured Bounties
- {featuredBounty ? (
- (window.location.href = featuredBounty.url)}>
- Bounty #{featuredBounty.bountyId}
-
- View Details
-
-
- ) : (
-
- No featured bounties at the moment
+
+
+ {featuredBounty ? (
+
+ ) : (
+
+ No featured bounties at the moment
+
+ )}
- )}
+
);
});
diff --git a/src/pages/BountiesLandingPage/index.tsx b/src/pages/BountiesLandingPage/index.tsx
index 2205fabe..9d537c16 100644
--- a/src/pages/BountiesLandingPage/index.tsx
+++ b/src/pages/BountiesLandingPage/index.tsx
@@ -26,8 +26,8 @@ const BountiesLandingPage: React.FC = () => {
`;
const ContentWrapper = styled.div`
- max-width: 1200px;
- min-height: 500px;
+ max-width: 1500px;
+ min-height: 650px;
margin: 30px auto;
width: 100%;
padding: 40px 20px 20px 30px;
@@ -44,7 +44,7 @@ const BountiesLandingPage: React.FC = () => {
&:after {
content: '';
position: absolute;
- left: 63%;
+ left: 68%;
top: 0;
bottom: 0;
width: 1px;
diff --git a/src/people/interfaces.ts b/src/people/interfaces.ts
index b4721885..1c997713 100644
--- a/src/people/interfaces.ts
+++ b/src/people/interfaces.ts
@@ -82,6 +82,7 @@ export interface BountiesProps {
img?: string;
id?: number;
activeWorkspace?: string;
+ isBountyLandingPage?: boolean;
}
export interface BadgesProps {
@@ -127,6 +128,7 @@ export interface NameTagProps {
org_name?: string;
org_uuid?: string;
uuid?: string;
+ isBountyLandingPage?: boolean;
}
export interface NoneSpaceProps {
@@ -413,6 +415,7 @@ export interface WantedViews2Props extends WantedViewsProps {
coding_languages?: any;
fromBountyPage?: boolean;
activeWorkspace?: string;
+ isBountyLandingPage?: boolean;
}
export interface AboutViewProps {
diff --git a/src/people/utils/AssignedUnassignedBounties.tsx b/src/people/utils/AssignedUnassignedBounties.tsx
index 96ce6055..890accef 100644
--- a/src/people/utils/AssignedUnassignedBounties.tsx
+++ b/src/people/utils/AssignedUnassignedBounties.tsx
@@ -20,6 +20,7 @@ interface containerProps {
unassigned_border?: string;
grayish_G200?: string;
color?: any;
+ isBountyLandingPage?: boolean;
}
const BountyContainer = styled.div
`
@@ -70,7 +71,7 @@ const DescriptionPriceContainer = styled.div`
`;
const UnassignedPersonProfile = styled.div`
- min-width: 336px;
+ min-width: ${(props: any) => (props.isBountyLandingPage ? '282px' : '336px')};
min-height: 160px;
max-height: auto;
background-image: url("data:image/svg+xml,%3csvg width='100%25' height='100%25' xmlns='http://www.w3.org/2000/svg'%3e%3crect width='100%25' height='100%25' fill='none' rx='10' ry='10' stroke='%23B0B7BCFF' stroke-width='3' stroke-dasharray='4' stroke-dashoffset='0' stroke-linecap='butt'/%3e%3c/svg%3e");
@@ -127,7 +128,8 @@ const Bounties = (props: BountiesProps) => {
widget,
created,
org_uuid,
- activeWorkspace
+ activeWorkspace,
+ isBountyLandingPage
} = props;
const color = colors['light'];
@@ -176,6 +178,7 @@ const Bounties = (props: BountiesProps) => {
org_img={props.img}
codingLanguage={codingLanguage}
created={created}
+ isBountyLandingPage={isBountyLandingPage}
/>
@@ -199,6 +202,7 @@ const Bounties = (props: BountiesProps) => {
height: '16px',
background: color.statusAssigned
}}
+ isBountyLandingPage={isBountyLandingPage}
/>
@@ -236,6 +240,7 @@ const Bounties = (props: BountiesProps) => {
maxWidth: '245px',
minWidth: '245px'
}}
+ isBountyLandingPage={isBountyLandingPage}
/>
@@ -243,6 +248,7 @@ const Bounties = (props: BountiesProps) => {
diff --git a/src/people/utils/NameTag.tsx b/src/people/utils/NameTag.tsx
index cef3c093..09ad43cf 100644
--- a/src/people/utils/NameTag.tsx
+++ b/src/people/utils/NameTag.tsx
@@ -57,6 +57,7 @@ const Date = styled.div`
`;
interface WrapProps {
readonly isSelected: boolean;
+ isBountyLandingPage?: boolean;
}
const Wrap = styled.div
`
@@ -65,6 +66,7 @@ const Wrap = styled.div`
cursor: ${(p: any) => !p.isSelected && 'pointer'};
width: fit-content;
margin-bottom: 10px;
+ margin-left: ${(p: any) => (p.isBountyLandingPage ? '95px' : '0px')};
color: #8e969c;
// &:hover {
// color: ${(p: any) => !p.isSelected && '#618AFF'};
@@ -83,7 +85,8 @@ function NameTag(props: NameTagProps) {
widget,
iconSize,
textSize,
- isPaid
+ isPaid,
+ isBountyLandingPage
} = props;
const { ui, main } = useStores();
const color = colors['light'];
@@ -153,7 +156,7 @@ function NameTag(props: NameTagProps) {
}
return (
-
+
)}
diff --git a/src/people/widgetViews/WidgetSwitchViewer.tsx b/src/people/widgetViews/WidgetSwitchViewer.tsx
index 4f8922fe..3b4a9e00 100644
--- a/src/people/widgetViews/WidgetSwitchViewer.tsx
+++ b/src/people/widgetViews/WidgetSwitchViewer.tsx
@@ -85,6 +85,7 @@ function WidgetSwitchViewer(props: any) {
page: propsPage,
setPage,
languageString,
+ isBountyLandingPage,
activeWorkspace,
uuid,
orgQueryLimit,
@@ -104,8 +105,8 @@ function WidgetSwitchViewer(props: any) {
minHeight: 132
}
: {
- minWidth: '1100px',
- maxWidth: '1100px',
+ minWidth: isBountyLandingPage ? '900px' : '1100px',
+ maxWidth: isBountyLandingPage ? '900px' : '1100px',
marginBottom: 16,
borderRadius: '10px',
display: 'flex',
@@ -298,6 +299,7 @@ function WidgetSwitchViewer(props: any) {
}}
person={person}
showModal={showModal}
+ isBountyLandingPage={isBountyLandingPage}
setDeletePayload={setDeletePayload}
fromBountyPage={props.fromBountyPage}
activeWorkspace={activeWorkspace}