From 98803f86667b5b8a60be3a8e1bdaf9dd4d1b9c24 Mon Sep 17 00:00:00 2001 From: reinaka Date: Thu, 25 Dec 2025 17:03:10 +0300 Subject: [PATCH 1/9] fix(88): add mobile version for add payment button, fix cards virtualization --- packages/pages.payments/src/hooks/index.ts | 1 + .../src/hooks/useVirtualCards.ts | 37 +++++++++++++++++++ packages/pages.payments/src/ui/Header.tsx | 6 ++- .../src/ui/Mobile/CardsList.tsx | 3 ++ .../pages.payments/src/ui/PaymentsPage.tsx | 10 ----- .../src/ui/VirtualizedPaymentsTable.tsx | 12 +++--- 6 files changed, 52 insertions(+), 17 deletions(-) create mode 100644 packages/pages.payments/src/hooks/useVirtualCards.ts diff --git a/packages/pages.payments/src/hooks/index.ts b/packages/pages.payments/src/hooks/index.ts index 5b25c615..113ecd7a 100644 --- a/packages/pages.payments/src/hooks/index.ts +++ b/packages/pages.payments/src/hooks/index.ts @@ -1,3 +1,4 @@ export * from './useResponsiveGrid'; export * from './useInfiniteScroll'; export * from './useVirtualGrid'; +export * from './useVirtualCards'; diff --git a/packages/pages.payments/src/hooks/useVirtualCards.ts b/packages/pages.payments/src/hooks/useVirtualCards.ts new file mode 100644 index 00000000..579ccefd --- /dev/null +++ b/packages/pages.payments/src/hooks/useVirtualCards.ts @@ -0,0 +1,37 @@ +import { RefObject, useLayoutEffect, useState, useCallback } from 'react'; +import { useVirtualizer, type Virtualizer } from '@tanstack/react-virtual'; + +export const useVirtualCards = ( + parentRef: RefObject, + items: T[], + estimatedCardHeight = 182, + overscan = 8, +) => { + const [cardHeights, setCardHeights] = useState>({}); + + const virtualizer: Virtualizer = useVirtualizer({ + count: items.length, + getScrollElement: () => parentRef.current, + estimateSize: (index) => cardHeights[index] ?? estimatedCardHeight, + overscan, + }); + + const measureCard = useCallback((index: number, el: HTMLDivElement | null) => { + if (!el) return; + + const height = el.getBoundingClientRect().height; + + setCardHeights((prev) => { + if (prev[index] === height) return prev; + return { ...prev, [index]: height }; + }); + }, []); + + useLayoutEffect(() => { + if (Object.keys(cardHeights).length > 0) { + virtualizer.measure(); + } + }, [cardHeights, virtualizer]); + + return { virtualizer, measureCard }; +}; diff --git a/packages/pages.payments/src/ui/Header.tsx b/packages/pages.payments/src/ui/Header.tsx index 69852f58..21e55107 100644 --- a/packages/pages.payments/src/ui/Header.tsx +++ b/packages/pages.payments/src/ui/Header.tsx @@ -1,5 +1,6 @@ import { Button } from '@xipkg/button'; import { useCurrentUser } from 'common.services'; +import { Plus } from '@xipkg/icons'; export const Header = ({ onCreateInvoice }: { onCreateInvoice: () => void }) => { const { data: user } = useCurrentUser(); @@ -12,10 +13,11 @@ export const Header = ({ onCreateInvoice }: { onCreateInvoice: () => void }) =>
)} diff --git a/packages/pages.payments/src/ui/Mobile/CardsList.tsx b/packages/pages.payments/src/ui/Mobile/CardsList.tsx index f91b031f..35becbae 100644 --- a/packages/pages.payments/src/ui/Mobile/CardsList.tsx +++ b/packages/pages.payments/src/ui/Mobile/CardsList.tsx @@ -7,6 +7,7 @@ import { Loader } from '../Loader'; export type CardsListPropsT = { data: PaymentDataT[]; rowVirtualizer: Virtualizer; + measureCard: (index: number, el: HTMLDivElement | null) => void; colCount: number; gap: number; parentRef: RefObject; @@ -18,6 +19,7 @@ export type CardsListPropsT = { export const CardsList = ({ data, rowVirtualizer, + measureCard, colCount, gap, parentRef, @@ -41,6 +43,7 @@ export const CardsList = ({ return (
measureCard(virtualRow.index, el)} style={{ position: 'absolute', top: 0, diff --git a/packages/pages.payments/src/ui/PaymentsPage.tsx b/packages/pages.payments/src/ui/PaymentsPage.tsx index e4b96e16..70884f52 100644 --- a/packages/pages.payments/src/ui/PaymentsPage.tsx +++ b/packages/pages.payments/src/ui/PaymentsPage.tsx @@ -1,6 +1,4 @@ import { useState, useCallback, useEffect, useRef, useMemo } from 'react'; -import { Button } from '@xipkg/button'; -import { Plus } from '@xipkg/icons'; import { InvoiceModal } from 'features.invoice'; import { PaymentApproveModal } from 'features.payment.approve'; import { RolePaymentT, useInfiniteQuery } from 'features.table'; @@ -158,14 +156,6 @@ export const PaymentsPage = () => {
-
- -
{paymentApproveModalState.isOpen && paymentApproveModalState.payment && ( Date: Sat, 27 Dec 2025 15:31:21 +0300 Subject: [PATCH 2/9] fix(88): add status badge, add useScreenSize hook --- packages/common.types/index.ts | 1 + packages/common.types/src/index.ts | 1 + packages/common.types/src/screenSize.ts | 1 + packages/common.utils/index.ts | 1 + packages/common.utils/package.json | 1 + packages/common.utils/src/useScreenSize.ts | 11 +++++++++ packages/features.invoice.card/index.ts | 2 +- packages/features.invoice.card/src/index.ts | 2 +- .../features.invoice.card/src/ui/index.ts | 2 ++ .../src/types/PaymentTypes.ts | 4 +++- .../src/ui/PaymentApproveAction.tsx | 3 ++- .../src/ui/PaymentApproveButton.tsx | 6 ++--- packages/features.table/package.json | 3 ++- .../src/ui/cells/StatusCell.tsx | 15 ++++++------ .../src/utils/createPaymentColumns.tsx | 12 ++++++---- packages/pages.classroom/package.json | 2 +- .../src/ui/Payments/Payments.tsx | 8 +++---- .../src/ui/Mobile/CardsList.tsx | 5 ++-- .../pages.payments/src/ui/TabsComponent.tsx | 8 +++---- .../src/ui/VirtualizedPaymentsTable.tsx | 2 +- pnpm-lock.yaml | 24 ++++++++++++------- 21 files changed, 70 insertions(+), 44 deletions(-) create mode 100644 packages/common.types/src/screenSize.ts create mode 100644 packages/common.utils/src/useScreenSize.ts create mode 100644 packages/features.invoice.card/src/ui/index.ts diff --git a/packages/common.types/index.ts b/packages/common.types/index.ts index b10057c0..00e60362 100644 --- a/packages/common.types/index.ts +++ b/packages/common.types/index.ts @@ -36,3 +36,4 @@ export type { InvitationDataT } from './src/invitations'; export type { GroupStudentsListSchema } from './src/students'; export type { ContactT, ContactsT } from './src/contacts'; export type { NotificationKind } from './src/notifications'; +export type { ScreenSizeT } from './src/screenSize'; diff --git a/packages/common.types/src/index.ts b/packages/common.types/src/index.ts index 309b1b07..153e50e8 100644 --- a/packages/common.types/src/index.ts +++ b/packages/common.types/src/index.ts @@ -42,3 +42,4 @@ export type { export type { SignupData } from './auth'; export type { ContactT, ContactsT } from './contacts'; export type { NotificationKind } from './notifications'; +export type { ScreenSizeT } from './screenSize'; diff --git a/packages/common.types/src/screenSize.ts b/packages/common.types/src/screenSize.ts new file mode 100644 index 00000000..6bbe7e57 --- /dev/null +++ b/packages/common.types/src/screenSize.ts @@ -0,0 +1 @@ +export type ScreenSizeT = 'mobile' | 'tablet' | 'desktop'; diff --git a/packages/common.utils/index.ts b/packages/common.utils/index.ts index f4be7c17..a538daa0 100644 --- a/packages/common.utils/index.ts +++ b/packages/common.utils/index.ts @@ -5,3 +5,4 @@ export { useMedia } from './src/useMedia'; export { useNetworkStatus, NetworkProvider } from './src/NetworkContext'; export { useRetryQueue } from './src/useRetryQueue'; export { getUserAvatarUrl } from './src/getUserAvatarUrl'; +export { useScreenSize } from './src/useScreenSize'; diff --git a/packages/common.utils/package.json b/packages/common.utils/package.json index da2dd179..0f25238d 100644 --- a/packages/common.utils/package.json +++ b/packages/common.utils/package.json @@ -20,6 +20,7 @@ "@xipkg/eslint": "3.2.0", "@xipkg/typescript": "latest", "common.eslint": "*", + "common.types": "*", "eslint": "^9.19.0", "eslint-plugin-react-hooks": "^5.0.0", "eslint-plugin-react-refresh": "^0.4.18", diff --git a/packages/common.utils/src/useScreenSize.ts b/packages/common.utils/src/useScreenSize.ts new file mode 100644 index 00000000..5e887f2a --- /dev/null +++ b/packages/common.utils/src/useScreenSize.ts @@ -0,0 +1,11 @@ +import { useMedia } from './useMedia'; +import type { ScreenSizeT } from 'common.types'; + +export const useScreenSize = (): ScreenSizeT => { + const isMobile = useMedia('(max-width: 720px)'); + const isTablet = useMedia('(max-width: 960px)'); + + if (isMobile) return 'mobile'; + if (isTablet) return 'tablet'; + return 'desktop'; +}; diff --git a/packages/features.invoice.card/index.ts b/packages/features.invoice.card/index.ts index d50933b1..88d7d983 100644 --- a/packages/features.invoice.card/index.ts +++ b/packages/features.invoice.card/index.ts @@ -1 +1 @@ -export { InvoiceCard } from './src'; +export { InvoiceCard, StatusBadge } from './src'; diff --git a/packages/features.invoice.card/src/index.ts b/packages/features.invoice.card/src/index.ts index 2521bdab..fcc6f11f 100644 --- a/packages/features.invoice.card/src/index.ts +++ b/packages/features.invoice.card/src/index.ts @@ -1 +1 @@ -export { InvoiceCard } from './ui/InvoiceCard'; +export { InvoiceCard, StatusBadge } from './ui'; diff --git a/packages/features.invoice.card/src/ui/index.ts b/packages/features.invoice.card/src/ui/index.ts new file mode 100644 index 00000000..0dcfb11e --- /dev/null +++ b/packages/features.invoice.card/src/ui/index.ts @@ -0,0 +1,2 @@ +export { InvoiceCard } from './InvoiceCard'; +export { StatusBadge } from './components'; diff --git a/packages/features.payment.approve/src/types/PaymentTypes.ts b/packages/features.payment.approve/src/types/PaymentTypes.ts index a5563793..2870393b 100644 --- a/packages/features.payment.approve/src/types/PaymentTypes.ts +++ b/packages/features.payment.approve/src/types/PaymentTypes.ts @@ -33,7 +33,9 @@ export type ApprovePaymentPropsT = { id?: number; }; -export type PaymentApproveActionPropsT = Pick; +export type PaymentApproveActionPropsT = Pick & { + type?: InvoiceCardTypeT; +}; export type PaymentApproveButtonPropsT = Omit & { type?: InvoiceCardTypeT; diff --git a/packages/features.payment.approve/src/ui/PaymentApproveAction.tsx b/packages/features.payment.approve/src/ui/PaymentApproveAction.tsx index 61e1dcc4..52c4bebb 100644 --- a/packages/features.payment.approve/src/ui/PaymentApproveAction.tsx +++ b/packages/features.payment.approve/src/ui/PaymentApproveAction.tsx @@ -2,7 +2,7 @@ import { useState } from 'react'; import { PaymentApproveButton, PaymentApproveModal } from '.'; import { PaymentApproveActionPropsT } from '../types'; -export const PaymentApproveAction = ({ payment, isTutor }: PaymentApproveActionPropsT) => { +export const PaymentApproveAction = ({ payment, isTutor, type }: PaymentApproveActionPropsT) => { const [isOpen, setIsOpen] = useState(false); const handleModalState = () => setIsOpen((prev) => !prev); @@ -17,6 +17,7 @@ export const PaymentApproveAction = ({ payment, isTutor }: PaymentApproveActionP isTutor={isTutor} id={payment.id} classroomId={payment.classroom_id} + type={type} /> {isOpen && payment && ( +
-
); }; diff --git a/packages/pages.classroom/src/ui/Tabs/TabsTutor.tsx b/packages/pages.classroom/src/ui/Tabs/TabsTutor.tsx index 1e26644a..896fd405 100644 --- a/packages/pages.classroom/src/ui/Tabs/TabsTutor.tsx +++ b/packages/pages.classroom/src/ui/Tabs/TabsTutor.tsx @@ -2,7 +2,7 @@ import { useState } from 'react'; import { Tabs } from '@xipkg/tabs'; import { useSearch, useNavigate, useParams } from '@tanstack/react-router'; - +import { Plus } from '@xipkg/icons'; import { Button } from '@xipkg/button'; import { Overview } from '../Overview'; import { SearchParams } from '../../types/router'; @@ -40,7 +40,7 @@ export const TabsTutor = () => { return ( -
+
Сводка @@ -60,14 +60,14 @@ export const TabsTutor = () => { {currentTab === 'overview' && classroom?.kind === 'group' && ( - )} {currentTab === 'overview' && classroom?.kind === 'group' && ( - @@ -76,10 +76,11 @@ export const TabsTutor = () => { {currentTab === 'payments' && ( )}
diff --git a/packages/pages.payments/src/ui/Mobile/CardsList.tsx b/packages/pages.payments/src/ui/Mobile/CardsList.tsx index 9e376540..9b54e35d 100644 --- a/packages/pages.payments/src/ui/Mobile/CardsList.tsx +++ b/packages/pages.payments/src/ui/Mobile/CardsList.tsx @@ -28,7 +28,7 @@ export const CardsList = ({ currentUserRole, }: CardsListPropsT) => { return ( -
+
Date: Mon, 29 Dec 2025 09:18:58 +0300 Subject: [PATCH 7/9] fix(88): render actions cell only if usersRole === 'student' --- .../features.table/src/utils/createPaymentColumns.tsx | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/packages/features.table/src/utils/createPaymentColumns.tsx b/packages/features.table/src/utils/createPaymentColumns.tsx index 1ff1a66d..52b21a88 100644 --- a/packages/features.table/src/utils/createPaymentColumns.tsx +++ b/packages/features.table/src/utils/createPaymentColumns.tsx @@ -113,14 +113,13 @@ export const createPaymentColumns = ({ filterFn: (row, columnId, value) => value.includes(row.getValue(columnId)), enableColumnFilter: true, }, - { + usersRole === 'student' && { accessorKey: 'actions', header: '', - cell: ({ row }) => - usersRole === 'student' ? ( - - ) : null, - size: usersRole === 'student' ? (screenSize === 'desktop' ? 96 : 40) : 0, + cell: ({ row }) => ( + + ), + size: screenSize === 'desktop' ? 96 : 40, filterFn: (row, columnId, value) => value.includes(row.getValue(columnId)), enableColumnFilter: false, }, From 73f888771df59ad82eaafd811c473765b806f510 Mon Sep 17 00:00:00 2001 From: reinaka Date: Sun, 4 Jan 2026 18:39:44 +0300 Subject: [PATCH 8/9] fix(88): fix styles --- packages/pages.payments/src/ui/Mobile/CardsList.tsx | 2 +- packages/pages.payments/src/ui/TabsComponent.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/pages.payments/src/ui/Mobile/CardsList.tsx b/packages/pages.payments/src/ui/Mobile/CardsList.tsx index 9b54e35d..063a61ec 100644 --- a/packages/pages.payments/src/ui/Mobile/CardsList.tsx +++ b/packages/pages.payments/src/ui/Mobile/CardsList.tsx @@ -28,7 +28,7 @@ export const CardsList = ({ currentUserRole, }: CardsListPropsT) => { return ( -
+
- + Журнал оплат From 2e4e7006a63563074ae54f9d570bfe3113d00c3e Mon Sep 17 00:00:00 2001 From: reinaka Date: Fri, 9 Jan 2026 21:03:45 +0300 Subject: [PATCH 9/9] fix(88): minor changes to classroom page tabs content styles --- packages/pages.classroom/src/ui/ClassroomPage.tsx | 2 +- packages/pages.classroom/src/ui/Header/components/Content.tsx | 2 +- packages/pages.classroom/src/ui/Information/Information.tsx | 2 +- packages/pages.classroom/src/ui/Tabs/TabsStudent.tsx | 2 +- packages/pages.classroom/src/ui/Tabs/TabsTutor.tsx | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/pages.classroom/src/ui/ClassroomPage.tsx b/packages/pages.classroom/src/ui/ClassroomPage.tsx index 0613a5a8..86224f12 100644 --- a/packages/pages.classroom/src/ui/ClassroomPage.tsx +++ b/packages/pages.classroom/src/ui/ClassroomPage.tsx @@ -3,7 +3,7 @@ import { Tabs } from './Tabs'; export const ClassroomPage = () => { return ( -
+
diff --git a/packages/pages.classroom/src/ui/Header/components/Content.tsx b/packages/pages.classroom/src/ui/Header/components/Content.tsx index 0ae1d34f..81ea3f0e 100644 --- a/packages/pages.classroom/src/ui/Header/components/Content.tsx +++ b/packages/pages.classroom/src/ui/Header/components/Content.tsx @@ -61,7 +61,7 @@ export const Content = ({ classroom }: ContentProps) => { }, [search, handleCallClick]); return ( -
+
{classroom.kind === 'individual' ? ( diff --git a/packages/pages.classroom/src/ui/Information/Information.tsx b/packages/pages.classroom/src/ui/Information/Information.tsx index 32bf80d8..9233557c 100644 --- a/packages/pages.classroom/src/ui/Information/Information.tsx +++ b/packages/pages.classroom/src/ui/Information/Information.tsx @@ -167,7 +167,7 @@ export const Information = ({ classroom }: { classroom: ClassroomT }) => { }, [form, onSubmit]); return ( -
+
diff --git a/packages/pages.classroom/src/ui/Tabs/TabsStudent.tsx b/packages/pages.classroom/src/ui/Tabs/TabsStudent.tsx index 920f6b61..bfb8ed1f 100644 --- a/packages/pages.classroom/src/ui/Tabs/TabsStudent.tsx +++ b/packages/pages.classroom/src/ui/Tabs/TabsStudent.tsx @@ -28,7 +28,7 @@ export const TabsStudent = () => { return ( -
+
Сводка diff --git a/packages/pages.classroom/src/ui/Tabs/TabsTutor.tsx b/packages/pages.classroom/src/ui/Tabs/TabsTutor.tsx index 896fd405..1c99e691 100644 --- a/packages/pages.classroom/src/ui/Tabs/TabsTutor.tsx +++ b/packages/pages.classroom/src/ui/Tabs/TabsTutor.tsx @@ -40,7 +40,7 @@ export const TabsTutor = () => { return ( -
+
Сводка