Skip to content

Commit

Permalink
687 add onboarding to develop (#694)
Browse files Browse the repository at this point in the history
* Install react joyride

* Fix error and icon display

* Add onboarding component

* Add onboarding to event page

* Add onboarding to results

* Update store with onboarding changes

* Add onboarding to results page

* Add onboarding to cycles page
  • Loading branch information
camilovegag authored Jul 22, 2024
1 parent cbcd143 commit 2963a25
Show file tree
Hide file tree
Showing 10 changed files with 603 additions and 103 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
"react-dom": "^18.2.0",
"react-hook-form": "^7.49.3",
"react-hot-toast": "^2.4.1",
"react-joyride": "^2.8.2",
"react-markdown": "^9.0.1",
"react-router-dom": "^6.20.1",
"styled-components": "^6.1.1",
Expand Down
73 changes: 73 additions & 0 deletions packages/berlin/src/components/onboarding/Onboarding.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { CSSProperties, ReactNode } from 'react';
import Joyride, { CallBackProps } from 'react-joyride';
import { useAppStore } from '../../store';

type OnboardingProps = {
type: 'event' | 'cycle' | 'results';
steps: {
target: string;
content: ReactNode;
}[];
};

const buttonBase: CSSProperties = {
fontFamily: 'var(--font-family-button)',
fontWeight: '500',
padding: '8px 16px',
textTransform: 'uppercase',
};

function Onboarding({ steps, type }: OnboardingProps) {
const { onboardingStatus, setOnboardingStatus } = useAppStore((state) => ({
onboardingStatus: state.onboardingStatus,
setOnboardingStatus: state.setOnboardingStatus,
}));

const run = onboardingStatus[type] !== 'COMPLETE';

const handleJoyrideCallback = (data: CallBackProps) => {
if (data.status === 'finished' || data.status === 'skipped') {
setOnboardingStatus(type, 'COMPLETE');
console.log('Onboarding completed for', type);
}
};

return (
<Joyride
steps={steps}
run={run}
continuous
showProgress
showSkipButton
hideCloseButton
callback={handleJoyrideCallback}
styles={{
buttonBack: {
...buttonBase,
backgroundColor: 'var(--color-white)',
color: 'var(--color-black)',
},
buttonNext: {
...buttonBase,
backgroundColor: 'var(--color-black)',
color: 'var(--color-white)',
},
buttonSkip: {
...buttonBase,
backgroundColor: 'var(--color-white)',
color: 'var(--color-black)',
},
buttonClose: {
color: 'var(--color-black)',
},
options: {
arrowColor: 'var(--color-white)',
backgroundColor: 'var(--color-white)',
primaryColor: 'var(--color-black)',
},
}}
/>
);
}

export default Onboarding;
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import styled from 'styled-components';
import { FlexColumn } from '../containers/FlexColumn.styled';

export const OnboardingCard = styled(FlexColumn)`
padding: 0rem;
background-color: var(--color-white);
text-align: left;
gap: 0.75rem;
`;
1 change: 1 addition & 0 deletions packages/berlin/src/components/onboarding/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from './Onboarding';
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { FlexColumn } from '../../containers/FlexColumn.styled';
import { FlexRow } from '../../containers/FlexRow.styled';
import IconButton from '../../icon-button';
import Link from '../../link';
import LucideIcon from '@/components/icon';

// Styled Components
import { Card, Funding, Icon, Plurality, TitleContainer } from './ResultsTable.styled';
Expand Down Expand Up @@ -180,11 +181,9 @@ function ResultsTable({ $expanded, option, onClick, cycleId, eventId }: ResultsT
<Body>
<Bold>Voter affiliations:</Bold> {option.listOfGroupNames.join(', ')}
</Body>
<Body>
<Icon>
<MessageSquareText onClick={handleCommentsClick} />
</Icon>
</Body>
<LucideIcon>
<MessageSquareText onClick={handleCommentsClick} />
</LucideIcon>
</FlexColumn>
</Card>
);
Expand Down
217 changes: 176 additions & 41 deletions packages/berlin/src/pages/Cycle.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@ import CycleColumns from '../components/columns/cycle-columns';
import OptionCard from '../components/option-card';
import { FINAL_QUESTION_TITLE, INITIAL_HEARTS } from '../utils/constants';
import { Heart } from 'lucide-react';
import { OnboardingCard } from '@/components/onboarding/Onboaring.styled';
import { Subtitle } from '@/components/typography/Subtitle.styled';
import IconButton from '@/components/icon-button';
import Onboarding from '../components/onboarding';
import Icon from '@/components/icon';

type Order = 'asc' | 'desc';
type LocalUserVotes = { optionId: string; numOfVotes: number }[];
Expand All @@ -60,6 +65,7 @@ function Cycle() {
const availableHearts =
useAppStore((state) => state.availableHearts[cycle?.forumQuestions[0].id || '']) ??
INITIAL_HEARTS;
const theme = useAppStore((state) => state.theme);
const setAvailableHearts = useAppStore((state) => state.setAvailableHearts);
const [startAt, setStartAt] = useState<string | null>(null);
const [endAt, setEndAt] = useState<string | null>(null);
Expand Down Expand Up @@ -292,49 +298,178 @@ function Cycle() {
);
};

return (
<FlexColumn $gap="2rem">
<FlexColumn>
<BackButton fallbackRoute={`/events/${eventId}/cycles`} />
<Title>{currentCycle?.questionTitle}</Title>
<Body>{voteInfo}</Body>
<Body>
You have <Bold>{availableHearts}</Bold> hearts left to give away:
</Body>
<FlexRow $gap="0.25rem" $wrap>
{Array.from({ length: INITIAL_HEARTS }).map((_, id) => (
<Heart key={id} fill={id < availableHearts ? '#ff0000' : 'none'} />
))}
</FlexRow>
<Button onClick={handleSaveVotesWrapper} disabled={!votesAreDifferent}>
Save all votes
</Button>
</FlexColumn>
{currentCycle?.questionOptions.length ? (
<FlexColumn $gap="0">
<CycleColumns onColumnClick={handleColumnClick} showScore={currentCycle.showScore} />
{sortedOptions.options.map((option) => {
const userVote = localUserVotes.find((vote) => vote.optionId === option.id);
const numOfVotes = userVote ? userVote.numOfVotes : 0;
return (
<OptionCard
key={option.id}
option={option}
numOfVotes={numOfVotes}
showFundingRequest={currentCycle.questionTitle === FINAL_QUESTION_TITLE}
showScore={currentCycle.showScore}
onVote={() => handleVoteWrapper(option.id)}
onUnVote={() => handleUnVoteWrapper(option.id)}
const steps = [
{
target: '.step-1',
content: (
<OnboardingCard>
<Subtitle>Voting Page</Subtitle>
<Body>View vote items and allocate your hearts.</Body>
</OnboardingCard>
),
placement: 'center',
},
{
target: '.step-2',
content: (
<OnboardingCard>
<Subtitle>Vote</Subtitle>
<FlexRow>
<FlexColumn $gap="-4px" style={{ width: 16 }}>
<IconButton
$padding={0}
$color="secondary"
icon={{ src: `/icons/upvote-${theme}.svg`, alt: 'Upvote arrow' }}
$width={16}
$height={16}
/>
);
})}
<IconButton
$padding={0}
$color="secondary"
icon={{ src: `/icons/downvote-${theme}.svg`, alt: 'Downvote arrow' }}
$width={16}
$height={16}
/>
</FlexColumn>
<Body>Upvote or downvote a vote item.</Body>
</FlexRow>
</OnboardingCard>
),
placement: 'center',
},
{
target: '.step-3',
content: (
<OnboardingCard>
<Subtitle>Save Your Votes</Subtitle>
<Body>
You must click the
<Button $color="secondary" style={{ paddingInline: 4 }}>
save all votes
</Button>{' '}
button or your vote will not be recorded.
</Body>
</OnboardingCard>
),
placement: 'center',
},
{
target: '.step-4',
content: (
<OnboardingCard>
<Subtitle>Information</Subtitle>
<Body>View vote item.</Body>
<FlexRow>
<Icon>
<Heart fill="#ff0000" />
</Icon>
<Body>Current number of hearts allocated to this vote item.</Body>
</FlexRow>
</OnboardingCard>
),
placement: 'center',
},
{
target: '.step-5',
content: (
<OnboardingCard>
<Subtitle>Voting Mechanisms</Subtitle>
<FlexRow>
<IconButton
$padding={0}
$color="secondary"
icon={{ src: `/icons/plurality-score.svg`, alt: 'Plurality score icon' }}
$width={24}
$height={24}
/>
<Body>
Plurality score, unlike quadratic score, considers pre-existing participant
relationships
</Body>
</FlexRow>
</OnboardingCard>
),
placement: 'center',
},
{
target: '.step-6',
content: (
<OnboardingCard>
<Subtitle>Expand a vote item</Subtitle>
<FlexRow>
<IconButton
$padding={0}
$color="secondary"
icon={{ src: `/icons/arrow-down-${theme}.svg`, alt: 'Arrow down icon' }}
$width={24}
$height={24}
/>
<Body>Click to view the vote item description and other useful information.</Body>
</FlexRow>
{/* <FlexRow>
<IconButton
$padding={0}
$color="secondary"
icon={{ src: `/icons/comments-${theme}.svg`, alt: 'Comments icon' }}
$width={24}
$height={24}
/>
<Body>
Click to view the comments page and start a discussion with other participants.
</Body>
</FlexRow> */}
</OnboardingCard>
),
placement: 'center',
},
];

return (
<>
<Onboarding steps={steps} type="cycle" />
<FlexColumn $gap="2rem" className="step-1 step-2 step-3 step-4 step-5 step-6">
<FlexColumn>
<BackButton fallbackRoute={`/events/${eventId}/cycles`} />
<Title>{currentCycle?.questionTitle}</Title>
<Body>{voteInfo}</Body>
<Body>
You have <Bold>{availableHearts}</Bold> hearts left to give away:
</Body>
<FlexRow $gap="0.25rem" $wrap>
{Array.from({ length: INITIAL_HEARTS }).map((_, id) => (
<Heart key={id} fill={id < availableHearts ? '#ff0000' : 'none'} />
))}
</FlexRow>
<Button onClick={handleSaveVotesWrapper} disabled={!votesAreDifferent}>
Save all votes
</Button>
</FlexColumn>
) : (
<Body>
<i>No options to show...</i>
</Body>
)}
</FlexColumn>
{currentCycle?.questionOptions.length ? (
<FlexColumn $gap="0">
<CycleColumns onColumnClick={handleColumnClick} showScore={currentCycle.showScore} />
{sortedOptions.options.map((option) => {
const userVote = localUserVotes.find((vote) => vote.optionId === option.id);
const numOfVotes = userVote ? userVote.numOfVotes : 0;
return (
<OptionCard
key={option.id}
option={option}
numOfVotes={numOfVotes}
showFundingRequest={currentCycle.questionTitle === FINAL_QUESTION_TITLE}
showScore={currentCycle.showScore}
onVote={() => handleVoteWrapper(option.id)}
onUnVote={() => handleUnVoteWrapper(option.id)}
/>
);
})}
</FlexColumn>
) : (
<Body>
<i>No options to show...</i>
</Body>
)}
</FlexColumn>
</>
);
}

Expand Down
Loading

0 comments on commit 2963a25

Please sign in to comment.