Skip to content

2027 new design payment drawer#2094

Open
vakac995 wants to merge 14 commits intopodkrepi-bg:masterfrom
vakac995:2027-new-design-payment-drawer
Open

2027 new design payment drawer#2094
vakac995 wants to merge 14 commits intopodkrepi-bg:masterfrom
vakac995:2027-new-design-payment-drawer

Conversation

@vakac995
Copy link
Contributor

Closes #2027

Motivation and context

Because requested here: #2027 (#2027)

I believe described here: #2027 (#2027)

By implementing requested changes from here: #2027 (#2027)

https://www.figma.com/design/MmvFKzUv6yE5U2wrOpWtwS/Podkrepi.bg?node-id=26830-46795&t=s1fFOpTTQPyWlov9-0

Screenshots:

Before After
Paste screenshot Paste screenshot
I do not have from before
desktop-donations I do not have from before| desktop-whishes I do not have from before| desktop-share I do not have from before| mobile-closedView I do not have from before| mobile-openView-donations I do not have from before| mobile-openView-whishes I do not have from before| mobile-share

campaigns/[slug]
campaigns/[slug]/share

Testing

Steps to test

Please check requested changes from here #2027 (#2027) and validate all is implemented

Affected urls

campaigns/[slug]
campaigns/[slug]/share

I did manual E2E tests

campaigns/[slug]
campaigns/[slug]/share

campaigns/[slug] - No longer shows Inline Donation but Payment Drawer

Environment

New environment variables:

  • NEW_ENV_VAR: env var details

New or updated dependencies:

Dependency name Previous version Updated version Details
dependency/name v1.0.0 v2.0.0

Refactor code structure for improved readability and maintainability
New redesigned payment drawer with mobile-first approach:

- Mobile: Fixed bottom bar with pie chart, expands via swipe-up gesture
- Desktop: Sticky sidebar with 360px max-width and drop shadow

Components added:
- PaymentDrawer: Main component with mobile/desktop responsive layouts
- CampaignProgressPie: SVG circular progress with state-based colors
- DonationsTab: Sortable donation list with API-based sorting
- WishesTab: Supporter wishes display with truncated messages
- ShareButtons: Facebook, email, WhatsApp, copy link functionality
- constants.ts: Centralized design tokens and styled components
- types.ts: TypeScript interfaces for all drawer components

Features:
- Swipe-to-open gesture with scroll prevention on mobile
- API-based sorting by date/amount for donations
- Tab navigation between Donations and Wishes
- Share buttons with clipboard integration
- Campaign progress pie chart with percentage display
- "See All" buttons scroll to wishes section

Replaces InlineDonation component with new PaymentDrawer

Relates to: podkrepi-bg#2027
New redesigned payment drawer with mobile-first approach:

PaymentDrawer:
- Mobile: Fixed bottom bar with pie chart, expands via swipe-up gesture
- Desktop: Sticky sidebar with 360px width and drop shadow
- Donations/Wishes tabs with sortable donation list
- API-based sorting by date/amount

SharePage (/campaigns/[slug]/share):
- Dedicated page for social sharing matching Figma design
- Campaign image with circular avatar display
- Facebook, LinkedIn, Instagram share buttons
- Yellow email button, grey copy link button
- Back navigation to campaign

Components added:
- PaymentDrawer, CampaignProgressPie, DonationsTab, WishesTab
- SharePage with social share handlers
- Design tokens in constants.ts, types in types.ts

Features:
- Swipe-to-open gesture with scroll prevention (mobile)
- Campaign progress pie chart with state-based colors
- "See All" buttons scroll to wishes section

Replaces InlineDonation component with PaymentDrawer

Relates to: podkrepi-bg#2027
- Wrap SharePage in Layout component with meta tags for SEO
- Remove back button and use site-wide navigation instead
- Change vertical alignment from center to top (flex-start)
- Adjust padding for desktop/mobile responsive design
- Remove unused imports (useRouter, ArrowBack)
- Set minHeight to 60vh instead of 100vh

Ref: podkrepi-bg#2027
- Add aria-hidden="true" to decorative swipe handle elements
- Replace hardcoded Bulgarian "от" with t('campaign.from') in WishesTab
…ns component is being used. Do we need it?"

No code changes needed. This is a comment regarding the usage of the ShareButtons component.
… bit redundant since all components here are used only by PaymentDrawer."

We only export PaymentDrawer from this file now.
…sure whether this would work. Can you remove it for now, as it is not in the requirements anyway."

Removed whatsapp share option from social media sharing helper.
…e/DesktopWrappers fragments into their own files? Should make the file a bit more readable."

PaymentDrawer Refactor - Modullized Components and Logic Hook; Separated concerns for better maintainability and readability.
…ographys etc..) should be within the themeOptions(common/theme.ts). Can you investigate whether it will be hard to do so, and adapt those settings(if not existing there)."

All theme related settings have been moved to the theme. Also, Added theme.d.ts for type safety.
- Add data-testid attributes to DesktopPaymentDrawer and MobilePaymentDrawer:
  - payment-drawer (wrapper)
  - summary-donors, summary-wishes (tabs)
  - summary-donors-wrapper, summary-wishes-wrapper (content)
  - payment-drawer-donate-button (donate button)

- Update campaigns.page.ts selectors:
  - Replace old InlineDonation selectors with PaymentDrawer testIds
  - Fix pre-existing TS errors in isDonorsSectionVisible/isWishesSectionVisible
    (methods referenced non-existent parent class methods since PR podkrepi-bg#1646)

- Fix campaign-view.spec.ts:
  - Add missing await before expect().toBeVisible() assertions

Refs: podkrepi-bg#2027
- Add 1px border using theme.palette.primary.light (#4AC3FF) to desktop drawer
- Implement separate page sizes: 7 for donations, 4 for wishes
- Remove unused isMobile parameter from usePaymentDrawerLogic hook
Copilot AI review requested due to automatic review settings January 30, 2026 10:22
@github-actions
Copy link

github-actions bot commented Jan 30, 2026

❌ Not all tests have run for this PR. Please add the run tests label to trigger them.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR implements a redesigned payment drawer for campaign pages per issue #2027. The redesign provides a modern, mobile-first interface with two states: collapsed (minimal info with progress indicator) and expanded (full details with donations/wishes tabs and sharing).

Changes:

  • Replaced InlineDonation component with new PaymentDrawer component featuring collapsed/expanded states for mobile and always-visible sidebar for desktop
  • Added new /campaigns/[slug]/share page with social media sharing functionality (Facebook, LinkedIn, Instagram, Email)
  • Extended API endpoints to support sorting donations by date or amount
  • Updated theme with new color variants, shadows, and gradients for the drawer design

Reviewed changes

Copilot reviewed 28 out of 29 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
src/components/client/campaigns/PaymentDrawer/* Complete redesign of donation interface with mobile/desktop variants, tabs, progress visualization, and integrated sharing
src/pages/campaigns/[slug]/share.tsx New dedicated share page with social media integration
src/components/client/campaigns/ViewCampaignPage.tsx Replaced InlineDonation with PaymentDrawer component
src/service/apiEndpoints.ts Added sortBy and sortOrder parameters to donations API endpoint
src/common/theme.ts & src/types/theme.d.ts Extended theme with accent colors, yellow variants, custom shadows, and gradients
src/common/routes.ts Added share route for campaigns
public/locales/{en,bg}/*.json Added translations for new drawer, tabs, sharing, and error messages
e2e/tests/regression/campaign-flow/campaign-view.spec.ts Updated tests with proper await syntax for new drawer selectors
e2e/pages/web-pages/campaigns/campaigns.page.ts Updated page object selectors to use PaymentDrawer instead of InlineDonation

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +206 to +218
const copyLink = useCallback(async () => {
try {
await copyToClipboard(campaignUrl)
AlertStore.show(
t('alerts.link-copied', { ns: 'common', defaultValue: 'Link copied successfully!' }),
'success',
)
} catch {
AlertStore.show(
t('alerts.copy-failed', { ns: 'common', defaultValue: 'Copy failed. Please try again.' }),
'error',
)
}
Copy link

Copilot AI Jan 30, 2026

Choose a reason for hiding this comment

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

The copyToClipboard function from useCopyToClipboard returns void, not a Promise. Using await and try-catch here won't work as expected. The function already handles success/failure internally through state management.

Replace this implementation with:

const copyLink = useCallback(() => {
  copyToClipboard(campaignUrl)
  AlertStore.show(
    t('alerts.link-copied', { ns: 'common', defaultValue: 'Link copied successfully!' }),
    'success',
  )
}, [campaignUrl, copyToClipboard, t])

Or if you need error handling, use the status from the hook:

const [copyStatus, copyToClipboard] = useCopyToClipboard(1000)

useEffect(() => {
  if (copyStatus === 'copied') {
    AlertStore.show(t('alerts.link-copied', { ns: 'common' }), 'success')
  } else if (copyStatus === 'failed') {
    AlertStore.show(t('alerts.copy-failed', { ns: 'common' }), 'error')
  }
}, [copyStatus, t])
Suggested change
const copyLink = useCallback(async () => {
try {
await copyToClipboard(campaignUrl)
AlertStore.show(
t('alerts.link-copied', { ns: 'common', defaultValue: 'Link copied successfully!' }),
'success',
)
} catch {
AlertStore.show(
t('alerts.copy-failed', { ns: 'common', defaultValue: 'Copy failed. Please try again.' }),
'error',
)
}
const copyLink = useCallback(() => {
copyToClipboard(campaignUrl)
AlertStore.show(
t('alerts.link-copied', { ns: 'common', defaultValue: 'Link copied successfully!' }),
'success',
)

Copilot uses AI. Check for mistakes.
onTouchStart={handleTouchStart}
onTouchEnd={handleTouchEnd}
data-testid="payment-drawer">
<Box className={classes.swipeHandle} aria-hidden="true" />
Copy link

Copilot AI Jan 30, 2026

Choose a reason for hiding this comment

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

The collapsed bottom bar relies solely on touch gestures (swipe up) to expand, which is not keyboard accessible. While the "Дарения" button inside can open the drawer, the swipe handle itself should be keyboard accessible for users who cannot use touch.

Consider wrapping the swipe handle in a button or adding keyboard event handlers:

<Box
  className={classes.swipeHandle}
  role="button"
  tabIndex={0}
  onClick={openDrawer}
  onKeyDown={(e) => {
    if (e.key === 'Enter' || e.key === ' ') {
      e.preventDefault()
      openDrawer()
    }
  }}
  aria-label={t('cta.expand-drawer', { defaultValue: 'Expand payment drawer' })}
/>

Alternatively, ensure the "Дарения" button is properly positioned as the first focusable element in the drawer for keyboard users.

Suggested change
<Box className={classes.swipeHandle} aria-hidden="true" />
<Box
className={classes.swipeHandle}
role="button"
tabIndex={0}
onClick={openDrawer}
onKeyDown={(e) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault()
openDrawer()
}
}}
aria-label={t('cta.expand-drawer', { defaultValue: 'Expand payment drawer' })}
/>

Copilot uses AI. Check for mistakes.
Comment on lines +66 to +87
<Box className={classes.desktopTabsWrapper}>
<Typography
className={`${classes.desktopTab} ${
activeTab === 'donors' ? classes.desktopTabSelected : ''
}`}
onClick={() => handleTabChange('donors')}
data-testid="summary-donors">
{t('cta.donations')}
</Typography>
<Typography
className={`${classes.desktopTab} ${
activeTab === 'wishes' ? classes.desktopTabSelected : ''
}`}
onClick={() => handleTabChange('wishes')}
data-testid="summary-wishes">
{t('campaign.wishes')}
</Typography>
</Box>

{/* Content */}
<Box
className={classes.contentSection}
Copy link

Copilot AI Jan 30, 2026

Choose a reason for hiding this comment

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

The tab elements use Typography components with onClick, which are not keyboard accessible. Users navigating with keyboards cannot activate these tabs using Enter or Space keys, and screen readers won't announce them as interactive elements.

Replace Typography with Button or add proper accessibility attributes:

<Button
  className={`${classes.desktopTab} ${
    activeTab === 'donors' ? classes.desktopTabSelected : ''
  }`}
  onClick={() => handleTabChange('donors')}
  role="tab"
  aria-selected={activeTab === 'donors'}
  aria-controls="summary-donors-wrapper"
  data-testid="summary-donors">
  {t('cta.donations')}
</Button>

Also wrap the tabs in a proper role="tablist" container. The same issue exists in MobilePaymentDrawer.tsx lines 195-208.

Suggested change
<Box className={classes.desktopTabsWrapper}>
<Typography
className={`${classes.desktopTab} ${
activeTab === 'donors' ? classes.desktopTabSelected : ''
}`}
onClick={() => handleTabChange('donors')}
data-testid="summary-donors">
{t('cta.donations')}
</Typography>
<Typography
className={`${classes.desktopTab} ${
activeTab === 'wishes' ? classes.desktopTabSelected : ''
}`}
onClick={() => handleTabChange('wishes')}
data-testid="summary-wishes">
{t('campaign.wishes')}
</Typography>
</Box>
{/* Content */}
<Box
className={classes.contentSection}
<Box className={classes.desktopTabsWrapper} role="tablist">
<Button
className={`${classes.desktopTab} ${
activeTab === 'donors' ? classes.desktopTabSelected : ''
}`}
onClick={() => handleTabChange('donors')}
role="tab"
aria-selected={activeTab === 'donors'}
aria-controls="summary-donors-wrapper"
data-testid="summary-donors">
{t('cta.donations')}
</Button>
<Button
className={`${classes.desktopTab} ${
activeTab === 'wishes' ? classes.desktopTabSelected : ''
}`}
onClick={() => handleTabChange('wishes')}
role="tab"
aria-selected={activeTab === 'wishes'}
aria-controls="summary-wishes-wrapper"
data-testid="summary-wishes">
{t('campaign.wishes')}
</Button>
</Box>
{/* Content */}
<Box
className={classes.contentSection}
id={activeTab === 'donors' ? 'summary-donors-wrapper' : 'summary-wishes-wrapper'}

Copilot uses AI. Check for mistakes.
Comment on lines +195 to +212
<FormControl className={classes.tabsContainer}>
<Typography
className={`${classes.tab} ${activeTab === 'donors' ? classes.tabSelected : ''}`}
onClick={() => handleTabChange('donors')}
data-testid="summary-donors">
{t('cta.donations')}
</Typography>
<Typography
className={`${classes.tab} ${activeTab === 'wishes' ? classes.tabSelected : ''}`}
onClick={() => handleTabChange('wishes')}
data-testid="summary-wishes">
{t('campaign.wishes')}
</Typography>
</FormControl>

{/* Content */}
<Box
className={classes.contentSection}
Copy link

Copilot AI Jan 30, 2026

Choose a reason for hiding this comment

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

The tab elements use Typography components with onClick, which are not keyboard accessible. Users navigating with keyboards cannot activate these tabs using Enter or Space keys, and screen readers won't announce them as interactive elements.

Replace Typography with Button or add proper accessibility attributes:

<Button
  className={`${classes.tab} ${activeTab === 'donors' ? classes.tabSelected : ''}`}
  onClick={() => handleTabChange('donors')}
  role="tab"
  aria-selected={activeTab === 'donors'}
  aria-controls="summary-donors-wrapper"
  data-testid="summary-donors">
  {t('cta.donations')}
</Button>

Also change the FormControl container to use role="tablist" for proper tab navigation semantics.

Suggested change
<FormControl className={classes.tabsContainer}>
<Typography
className={`${classes.tab} ${activeTab === 'donors' ? classes.tabSelected : ''}`}
onClick={() => handleTabChange('donors')}
data-testid="summary-donors">
{t('cta.donations')}
</Typography>
<Typography
className={`${classes.tab} ${activeTab === 'wishes' ? classes.tabSelected : ''}`}
onClick={() => handleTabChange('wishes')}
data-testid="summary-wishes">
{t('campaign.wishes')}
</Typography>
</FormControl>
{/* Content */}
<Box
className={classes.contentSection}
<FormControl className={classes.tabsContainer} role="tablist">
<Button
type="button"
className={`${classes.tab} ${activeTab === 'donors' ? classes.tabSelected : ''}`}
onClick={() => handleTabChange('donors')}
role="tab"
aria-selected={activeTab === 'donors'}
aria-controls="summary-donors-wrapper"
data-testid="summary-donors">
{t('cta.donations')}
</Button>
<Button
type="button"
className={`${classes.tab} ${activeTab === 'wishes' ? classes.tabSelected : ''}`}
onClick={() => handleTabChange('wishes')}
role="tab"
aria-selected={activeTab === 'wishes'}
aria-controls="summary-wishes-wrapper"
data-testid="summary-wishes">
{t('campaign.wishes')}
</Button>
</FormControl>
{/* Content */}
<Box
className={classes.contentSection}
id={
activeTab === 'donors' ? 'summary-donors-wrapper' : 'summary-wishes-wrapper'
}

Copilot uses AI. Check for mistakes.
Comment on lines +18 to +26
const colors = {
white: '#FFFFFF',
black: '#000000',
darkBlue: '#294E85',
yellow: '#FFCB57',
yellowHover: '#FFCA28',
greyBackground: '#F5F5F5',
greyBorder: '#E0E0E0',
}
Copy link

Copilot AI Jan 30, 2026

Choose a reason for hiding this comment

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

Hardcoded colors are used instead of theme values, which reduces maintainability and consistency across the application. Several of these colors are already defined in the theme.

Replace hardcoded color values with theme references:

  • colors.yellowtheme.palette.yellowVariants.light or theme.palette.secondary.main
  • colors.yellowHovertheme.palette.yellowVariants.hover
  • colors.whitetheme.palette.common.white
  • colors.blacktheme.palette.common.black
  • colors.greyBackgroundtheme.palette.grey[100]
  • colors.greyBordertheme.palette.grey[300]

The darkBlue (#294E85) appears to match the primary dark blue used elsewhere. Consider adding it to the theme if it's a brand color, or use an existing theme color.

Copilot uses AI. Check for mistakes.
@sashko9807
Copy link
Member

Apologizes for the delay.
Those seems a lot of file changes, just for a small adjustments in the padding as described in Discord. Is there anything else that has been changed here?
If not, can you just merge the working branch with master and commit only the changes? If you think it will be easier, feel free to open another PR of this with just the padding change.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

New design payment drawer

2 participants