diff --git a/src/components/header/components/notificationsDropdown/NotificationsDropdown.tsx b/src/components/header/components/notificationsDropdown/NotificationsDropdown.tsx index 2b38fbe3..0c768b70 100644 --- a/src/components/header/components/notificationsDropdown/NotificationsDropdown.tsx +++ b/src/components/header/components/notificationsDropdown/NotificationsDropdown.tsx @@ -135,7 +135,7 @@ export const NotificationsDropdown: React.FC = () => { +
{ const btc = satoshis / 100000000; return ( -
-
{satoshis.toLocaleString()} sats
+
+
{satoshis.toLocaleString()} sats
({btc.toFixed(8)} BTC)
@@ -48,7 +45,7 @@ export const PaymentNotificationsOverlay: React.FC ( - +
{formatDate(notification.created_at)}
- {t('payment.notifications.amount', 'Amount')}: {formatAmount(notification.amount)}
+
-
+ {/*
{t('payment.notifications.expiration', 'Expires')}: {formatDate(notification.expiration_date)} -
- +
*/} + + + {t('payment.notifications.viewDetails', 'View details')} + {!notification.is_read && ( markAsRead(notification.id)} - style={{ padding: '4px 0', height: 'auto', marginTop: '4px' }} + style={{ padding: '4px 0', height: 'auto', marginTop: '4px', fontSize: '0.85rem' }} > {t('payment.notifications.markAsRead', 'Mark as read')} )} -
- - {t('payment.notifications.viewDetails', 'View details')} - -
+ +
} /> @@ -129,9 +127,9 @@ export const PaymentNotificationsOverlay: React.FC {notifications.length > 0 ? ( - }> + } style={{ width: '95%' }}> {noticesList} - + ) : (
diff --git a/src/components/payment/PaymentNotifications/PaymentNotifications.styles.ts b/src/components/payment/PaymentNotifications/PaymentNotifications.styles.ts index d56e8ddc..f3febf04 100644 --- a/src/components/payment/PaymentNotifications/PaymentNotifications.styles.ts +++ b/src/components/payment/PaymentNotifications/PaymentNotifications.styles.ts @@ -1,8 +1,11 @@ import styled from 'styled-components'; +import { BaseCard } from '@app/components/common/BaseCard/BaseCard'; import { BaseButton } from '@app/components/common/BaseButton/BaseButton'; import { BaseTypography } from '@app/components/common/BaseTypography/BaseTypography'; import { BaseInput } from '@app/components/common/inputs/BaseInput/BaseInput'; -import { BORDER_RADIUS, FONT_SIZE, FONT_WEIGHT } from '@app/styles/themes/constants'; +import { BORDER_RADIUS, FONT_SIZE, FONT_WEIGHT, media } from '@app/styles/themes/constants'; +import { Card } from 'antd'; +import { BaseRow } from '@app/components/common/BaseRow/BaseRow'; export const FiltersWrapper = styled.div` margin-bottom: 1.5rem; @@ -18,43 +21,61 @@ export const SplitDivider = styled.div` width: 100%; `; -export const NotificationItem = styled.div<{ $isRead: boolean; $isNew?: boolean }>` +export const NotificationItem = styled(Card)<{ $isRead: boolean; $isNew?: boolean }>` position: relative; transition: all 0.3s ease; - + width: 100%; + background-color: var(--additional-background-color); + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.5); + border: none; + .ant-space { + background-color: transparent; + } + .anticon-info-circle { + color: var(--text-light-color); + width: 2.4rem; + padding: 0 0.2rem; + } ${(props) => !props.$isRead && ` - background-color: var(--background-color); border-radius: ${BORDER_RADIUS}; position: relative; &::before { content: ''; position: absolute; - left: -0.5rem; - top: 50%; + left: -1.1rem; + top: 2.4rem; transform: translateY(-50%); width: 0.5rem; height: 0.5rem; border-radius: 50%; background-color: var(--primary-color); + + @media only screen and ${media.lg} { + left: -2rem; + } } `} ${(props) => props.$isNew && ` - border-left: 3px solid var(--success-color); + border-left: 3px solid var(--success-color) !important; `} `; - +export const NotificationHeader = styled(BaseRow)` + color: var(--text-main-color); + font-size: ${FONT_SIZE.md}; +`; export const NotificationContent = styled.div` padding-top: 0.5rem; `; export const NotificationMeta = styled.div` display: flex; + flex-wrap: wrap; gap: 0.75rem; margin-bottom: 0.5rem; @@ -70,11 +91,17 @@ export const MetaItem = styled.div` export const MetaLabel = styled.span` font-weight: ${FONT_WEIGHT.semibold}; margin-right: 0.5rem; + font-size: ${FONT_SIZE.md}; `; export const MetaValue = styled.span` display: inline-flex; + font-size: ${FONT_SIZE.md}; align-items: center; + &:not(.date){ + color: var(--text-main-color); + } + `; export const CopyButton = styled(BaseButton)` @@ -84,23 +111,24 @@ export const CopyButton = styled(BaseButton)` justify-content: center; font-size: ${FONT_SIZE.xs}; padding: 2px 6px; - height: 20px; + height: 24px; border-radius: ${BORDER_RADIUS}; background-color: var(--background-color); - + &:hover { background-color: var(--secondary-background-color); } `; export const MarkReadButton = styled(BaseButton)` - margin-top: 0.75rem; padding: 0; height: auto; + font-size: ${FONT_SIZE.xs}; `; export const UserInput = styled(BaseInput)` width: 100%; + background-color: var(--input-bg-color); `; export const Text = styled(BaseTypography.Text)` @@ -122,19 +150,23 @@ export const TierTag = styled.span<{ $tier: string }>` `; export const NewSubscriberBadge = styled.span` - display: inline-block; - padding: 0.25rem 0.5rem; + display: flex; + align-items: center; + padding: 0.5rem; border-radius: ${BORDER_RADIUS}; font-size: ${FONT_SIZE.xs}; font-weight: ${FONT_WEIGHT.semibold}; margin-left: 0.5rem; background-color: var(--success-color); + height: 1.5rem; color: var(--text-secondary-color); `; export const AmountDisplay = styled.div` display: flex; + font-size: ${FONT_SIZE.lg}; flex-direction: column; + align-items: flex-end; `; export const SatAmount = styled.span` @@ -150,12 +182,56 @@ export const ExpirationInfo = styled.div` margin-top: 0.5rem; font-size: ${FONT_SIZE.xs}; color: var(--text-light-color); - - ${(props) => props.color === 'warning' && ` + font-weight: ${FONT_WEIGHT.regular}; + + ${(props) => + props.color === 'warning' && + ` color: var(--warning-color); `} - - ${(props) => props.color === 'error' && ` + + ${(props) => + props.color === 'error' && + ` color: var(--error-color); `} `; +export const Root = styled(BaseCard)` + padding: 0; + padding-top: 1.25rem; + min-width: fit-content; + + .ant-space.ant-space-horizontal { + width: 100%; + } + margin-left: 5%; + margin-right: 5%; + @media only screen and ${media.lg} { + > .ant-card-head, + > .ant-card-body { + margin-left: 18%; + margin-right: 18%; + } + } +`; +export const TransactionWrapper = styled.div` + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 1.5rem; + margin-top: 1.5rem; + min-width: 65vw; + @media only screen and ${media.lg} { + min-width: 40vw; +`; +export const LeftSideTX = styled.div` + display: flex; + flex-direction: column; + gap: 0.25rem; +`; + +export const CardFooter = styled.div` + display: flex; + justify-content: space-between; + align-items: center; +`; diff --git a/src/components/payment/PaymentNotifications/PaymentNotifications.tsx b/src/components/payment/PaymentNotifications/PaymentNotifications.tsx index efe08320..b9d1a60c 100644 --- a/src/components/payment/PaymentNotifications/PaymentNotifications.tsx +++ b/src/components/payment/PaymentNotifications/PaymentNotifications.tsx @@ -1,6 +1,5 @@ import React, { useState, useEffect } from 'react'; import { useTranslation } from 'react-i18next'; -import { BaseCard } from '@app/components/common/BaseCard/BaseCard'; import { BaseButton } from '@app/components/common/BaseButton/BaseButton'; import { BaseSelect } from '@app/components/common/selects/BaseSelect/BaseSelect'; import { BaseSpace } from '@app/components/common/BaseSpace/BaseSpace'; @@ -8,9 +7,8 @@ import { BaseRow } from '@app/components/common/BaseRow/BaseRow'; import { BaseCol } from '@app/components/common/BaseCol/BaseCol'; import { BasePagination } from '@app/components/common/BasePagination/BasePagination'; import { BaseNotification } from '@app/components/common/BaseNotification/BaseNotification'; -import { BaseInput } from '@app/components/common/inputs/BaseInput/BaseInput'; import { usePaymentNotifications } from '@app/hooks/usePaymentNotifications'; -import { PaymentNotification, PaymentNotificationParams } from '@app/api/paymentNotifications.api'; +import { PaymentNotificationParams } from '@app/api/paymentNotifications.api'; import { notificationController } from '@app/controllers/notificationController'; import * as S from './PaymentNotifications.styles'; @@ -23,35 +21,29 @@ export const PaymentNotifications: React.FC = ({ clas const [filter, setFilter] = useState<'all' | 'unread' | 'user'>('unread'); const [userPubkey, setUserPubkey] = useState(''); - const { - notifications, - pagination, - isLoading, - fetchNotifications, - markAsRead, - markAllAsRead - } = usePaymentNotifications(); - + const { notifications, pagination, isLoading, fetchNotifications, markAsRead, markAllAsRead } = + usePaymentNotifications(); + // Fetch unread notifications on component mount useEffect(() => { fetchNotifications({ page: 1, limit: pagination?.pageSize || 10, - filter: 'unread' + filter: 'unread', }); }, [fetchNotifications, pagination?.pageSize]); const handleFilterChange = (value: unknown) => { const filterValue = value as 'all' | 'unread' | 'user'; setFilter(filterValue); - + // Only fetch immediately for "all" and "unread" filters // For "user" filter, wait for the user to click the Filter button if (filterValue !== 'user') { fetchNotifications({ page: 1, limit: pagination?.pageSize || 10, - filter: filterValue + filter: filterValue, }); } }; @@ -60,13 +52,13 @@ export const PaymentNotifications: React.FC = ({ clas const params: PaymentNotificationParams = { page, limit: pagination?.pageSize || 10, - filter + filter, }; - + if (filter === 'user' && userPubkey) { params.pubkey = userPubkey; } - + fetchNotifications(params); }; @@ -80,7 +72,7 @@ export const PaymentNotifications: React.FC = ({ clas page: 1, limit: pagination?.pageSize || 10, filter: 'user', - pubkey: userPubkey + pubkey: userPubkey, }); } }; @@ -104,50 +96,55 @@ export const PaymentNotifications: React.FC = ({ clas const expiration = new Date(dateString); const now = new Date(); const daysUntilExpiration = Math.floor((expiration.getTime() - now.getTime()) / (1000 * 60 * 60 * 24)); - + let color: 'default' | 'warning' | 'error' = 'default'; if (daysUntilExpiration <= 3) { color = 'error'; } else if (daysUntilExpiration <= 7) { color = 'warning'; } - + return ( - {daysUntilExpiration <= 0 + {daysUntilExpiration <= 0 ? t('payment.notifications.expired', 'Expired') : t('payment.notifications.expiresInDays', 'Expires in {{days}} days', { days: daysUntilExpiration })} - {' - '}{formatDate(dateString)} + {' - '} + {formatDate(dateString)} ); }; return ( - + - - + - + {filter === 'user' && ( <> - - + - + {t('payment.notifications.filter', 'Filter')} @@ -160,58 +157,45 @@ export const PaymentNotifications: React.FC = ({ clas {isLoading ? (
- - {t('common.loading', 'Loading...')} - + {t('common.loading', 'Loading...')}
) : notifications.length > 0 ? ( <> - }> +
}> {notifications.map((notification) => ( - - - {notification.subscription_tier} - + + {notification.subscription_tier} {t( - notification.is_new_subscriber - ? 'payment.notifications.newSubscription' - : 'payment.notifications.renewalSubscription', - notification.is_new_subscriber - ? 'New Subscription' - : 'Subscription Renewal' + notification.is_new_subscriber + ? 'payment.notifications.newSubscription' + : 'payment.notifications.renewalSubscription', + notification.is_new_subscriber ? 'New Subscription' : 'Subscription Renewal', )} {notification.is_new_subscriber && ( - - {t('payment.notifications.new', 'NEW')} - + {t('payment.notifications.new', 'NEW')} )} - + } description={ - - Date: - {formatDate(notification.created_at)} - - User: {notification.pubkey.substring(0, 10)}... - { navigator.clipboard.writeText(notification.pubkey); notificationController.success({ - message: 'User pubkey copied to clipboard' + message: 'User pubkey copied to clipboard', }); }} > @@ -219,16 +203,15 @@ export const PaymentNotifications: React.FC = ({ clas - TX ID: {notification.tx_id.substring(0, 10)}... - { navigator.clipboard.writeText(notification.tx_id); notificationController.success({ - message: 'Transaction ID copied to clipboard' + message: 'Transaction ID copied to clipboard', }); }} > @@ -237,23 +220,23 @@ export const PaymentNotifications: React.FC = ({ clas - -
- {t('payment.notifications.amount', 'Amount')}: - {formatAmount(notification.amount)} -
- - {formatExpirationDate(notification.expiration_date)} - - {!notification.is_read && ( - markAsRead(notification.id)} - size="small" - type="link" - > - {t('payment.notifications.markAsRead', 'Mark as read')} - - )} + + + + {formatDate(notification.created_at)} + + +
{formatAmount(notification.amount)}
+
+ + {formatExpirationDate(notification.expiration_date)} + + {!notification.is_read && ( + markAsRead(notification.id)} size="small" type="link"> + {t('payment.notifications.markAsRead', 'Mark as read')} + + )} +
} /> @@ -264,7 +247,7 @@ export const PaymentNotifications: React.FC = ({ clas - {notifications.some(n => !n.is_read) && ( + {notifications.some((n) => !n.is_read) && ( markAllAsRead()}> {t('payment.notifications.readAll', 'Mark all as read')} @@ -285,16 +268,37 @@ export const PaymentNotifications: React.FC = ({ clas ) : ( -
+
💰
{t('payment.notifications.noNotifications', 'No payment notifications')} - - {t('payment.notifications.emptyDescription', 'Payment notifications will appear here when users subscribe to your services')} + + {t( + 'payment.notifications.emptyDescription', + 'Payment notifications will appear here when users subscribe to your services', + )}
)} - + ); }; diff --git a/src/components/settings/SettingsPage.tsx b/src/components/settings/SettingsPage.tsx index 3e21e3c1..d8eccc82 100644 --- a/src/components/settings/SettingsPage.tsx +++ b/src/components/settings/SettingsPage.tsx @@ -5,7 +5,6 @@ import styled from 'styled-components'; import { FilterOutlined, PictureOutlined, - ApiOutlined, RobotOutlined, InfoCircleOutlined, WalletOutlined, diff --git a/src/styles/themes/dark/darkTheme.ts b/src/styles/themes/dark/darkTheme.ts index d7d7dde8..4de96e85 100644 --- a/src/styles/themes/dark/darkTheme.ts +++ b/src/styles/themes/dark/darkTheme.ts @@ -98,6 +98,7 @@ export const darkColorsTheme: ITheme = { layoutHeaderBg: '#1e2142', layoutSiderBg: '#121430', inputPlaceholder: 'rgba(255, 255, 255, 0.5)', + inputBg: `rgba(0, 0, 0, 0.2)`, itemHoverBg: '#1c2137', backgroundColorBase: '#1c2137', avatarBg: '#1c2137', diff --git a/src/styles/themes/light/lightTheme.ts b/src/styles/themes/light/lightTheme.ts index 43490c9a..36dc30ba 100644 --- a/src/styles/themes/light/lightTheme.ts +++ b/src/styles/themes/light/lightTheme.ts @@ -98,6 +98,7 @@ export const lightColorsTheme: ITheme = { layoutHeaderBg: 'transparent', layoutSiderBg: 'linear-gradient(261.31deg, #006ccf -29.57%, #00509a 121.11%)', inputPlaceholder: '#404040', + inputBg: 'rgba(215, 215, 215, 0.9)', itemHoverBg: '#f5f5f5', backgroundColorBase: '#F5F5F5', avatarBg: '#ccc', diff --git a/src/styles/themes/themeVariables.ts b/src/styles/themes/themeVariables.ts index 1c38f371..ee009e36 100644 --- a/src/styles/themes/themeVariables.ts +++ b/src/styles/themes/themeVariables.ts @@ -97,6 +97,7 @@ const getThemeVariables = (theme: ThemeType) => css` --layout-header-bg-color: ${themeObject[theme].layoutHeaderBg}; --layout-sider-bg-color: ${themeObject[theme].layoutSiderBg}; --input-placeholder-color: ${themeObject[theme].inputPlaceholder}; + --input-bg-color: ${themeObject[theme].inputBg}; --avatar-bg: ${themeObject[theme].avatarBg}; --alert-text-color: ${themeObject[theme].alertTextColor}; --breadcrumb-color: ${themeObject[theme].breadcrumb}; diff --git a/src/styles/themes/types.ts b/src/styles/themes/types.ts index 70e68ade..61166300 100644 --- a/src/styles/themes/types.ts +++ b/src/styles/themes/types.ts @@ -64,6 +64,7 @@ export interface ITheme { layoutHeaderBg: string; layoutSiderBg: string; inputPlaceholder: string; + inputBg: string; itemHoverBg: string; backgroundColorBase: string; avatarBg: string;