Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
302f7db
feat(DataList): add row selection functionality with checkboxes
lukasz-hycom Dec 4, 2025
21df162
Merge branch 'main' of https://github.com/o2sdev/openselfservice into…
lukasz-hycom Dec 4, 2025
7d9f2cc
feat: implement row selection feature across multiple list blocks
lukasz-hycom Dec 5, 2025
af760f9
Merge branch 'main' of https://github.com/o2sdev/openselfservice into…
lukasz-hycom Dec 5, 2025
7ba37e3
feat: add bulk download functionality and improve row selection in in…
lukasz-hycom Dec 8, 2025
b8c8ea4
feat: adding new fields in cms for invoice-list block
lukasz-hycom Dec 8, 2025
f1b9062
refactor: simplify bulk actions handling in DataView and InvoiceList …
lukasz-hycom Dec 8, 2025
deffd23
refactor: enhance bulk actions logic in InvoiceList and DataView comp…
lukasz-hycom Dec 8, 2025
ba18145
Merge branch 'main' of https://github.com/o2sdev/openselfservice into…
lukasz-hycom Dec 8, 2025
e117f7a
Merge remote-tracking branch 'origin/main' into feature/adding-checkb…
marcinkrasowski Dec 18, 2025
5f943c3
fix: fixed incorrect price rendering in `DataView` component
marcinkrasowski Dec 18, 2025
6290a35
fix: removed unnecessary prop destructuring
marcinkrasowski Dec 18, 2025
bc9e361
fix: formatting and imports improvements
marcinkrasowski Dec 18, 2025
0842bcc
fix: added missing values
marcinkrasowski Dec 18, 2025
ef740d5
feat: wrapped checking for selected rows with `useMemo`
marcinkrasowski Dec 18, 2025
973ad93
feat: aligned logic for selecting rows
marcinkrasowski Dec 18, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/mean-shoes-behave.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@o2s/blocks.invoice-list': patch
'@o2s/ui': patch
---

fixed incorrect price rendering in `DataView` component
5 changes: 5 additions & 0 deletions .changeset/shy-bobcats-count.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@o2s/ui': patch
---

removed unnecessary prop destructuring
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ export const mapInvoiceList = (
downloadButtonAriaDescription: cms.downloadButtonAriaDescription,
initialFilters: cms.initialFilters,
cardHeaderSlots: cms.cardHeaderSlots,
enableRowSelection: cms.enableRowSelection,
bulkActionsLabel: cms.bulkActionsLabel,
downloadAllButtonLabel: cms.downloadAllButtonLabel,
};
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ export class InvoiceListBlock extends ApiModels.Block.Block {
right?: string;
bottom?: string;
};
enableRowSelection?: boolean;
bulkActionsLabel?: string;
downloadAllButtonLabel?: string;
}

export class Invoice {
Expand All @@ -47,10 +50,6 @@ export class Invoice {
value: Invoices.Model.Invoice['paymentDueDate'];
displayValue: string;
};
totalAmountDue!: {
value: Invoices.Model.Invoice['totalAmountDue']['value'];
};
totalNetAmountDue!: {
value: Invoices.Model.Invoice['totalNetAmountDue']['value'];
};
totalAmountDue!: Invoices.Model.Invoice['totalAmountDue'];
totalNetAmountDue!: Invoices.Model.Invoice['totalNetAmountDue'];
}
Original file line number Diff line number Diff line change
Expand Up @@ -175,9 +175,11 @@ export const Default: Story = {
},
totalAmountDue: {
value: 1250.5,
currency: 'EUR',
},
totalNetAmountDue: {
value: 1016.67,
currency: 'EUR',
},
paymentDueDate: {
displayValue: '07/15/2024',
Expand All @@ -197,9 +199,11 @@ export const Default: Story = {
},
totalAmountDue: {
value: 3450.75,
currency: 'EUR',
},
totalNetAmountDue: {
value: 2805.49,
currency: 'EUR',
},
paymentDueDate: {
displayValue: '06/10/2024',
Expand All @@ -219,9 +223,11 @@ export const Default: Story = {
},
totalAmountDue: {
value: 780.25,
currency: 'EUR',
},
totalNetAmountDue: {
value: 634.35,
currency: 'EUR',
},
paymentDueDate: {
displayValue: '07/05/2024',
Expand All @@ -241,9 +247,11 @@ export const Default: Story = {
},
totalAmountDue: {
value: 0,
currency: 'EUR',
},
totalNetAmountDue: {
value: 0,
currency: 'EUR',
},
paymentDueDate: {
displayValue: '05/20/2024',
Expand All @@ -263,9 +271,11 @@ export const Default: Story = {
},
totalAmountDue: {
value: 5670.3,
currency: 'EUR',
},
totalNetAmountDue: {
value: 4610,
currency: 'EUR',
},
paymentDueDate: {
displayValue: '06/15/2024',
Expand Down
57 changes: 57 additions & 0 deletions packages/blocks/invoice-list/src/frontend/InvoiceList.client.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
'use client';

import { IntlMessageFormat } from 'intl-messageformat';
import { Download } from 'lucide-react';
import { useLocale } from 'next-intl';
import React, { useState, useTransition } from 'react';

import { Mappings, Utils } from '@o2s/utils.frontend';
Expand All @@ -27,6 +29,7 @@ import { InvoiceListPureProps } from './InvoiceList.types';

export const InvoiceListPure: React.FC<InvoiceListPureProps> = ({ locale, accessToken, routing, ...component }) => {
const { labels } = useGlobalContext();
const currentLocale = useLocale();

const initialFilters: Request.GetInvoiceListBlockQuery = {
id: component.id,
Expand All @@ -44,6 +47,7 @@ export const InvoiceListPure: React.FC<InvoiceListPureProps> = ({ locale, access
const [data, setData] = useState(component);
const [filters, setFilters] = useState(initialFilters);
const [viewMode, setViewMode] = useState<'list' | 'grid'>(initialViewMode);
const [selectedRows, setSelectedRows] = useState<Set<string | number>>(new Set());
const [isPending, startTransition] = useTransition();

const handleFilter = (data: Partial<Request.GetInvoiceListBlockQuery>) => {
Expand Down Expand Up @@ -71,6 +75,7 @@ export const InvoiceListPure: React.FC<InvoiceListPureProps> = ({ locale, access

setFilters(initialFilters);
setData(newData);
setSelectedRows(new Set());
} catch (_error) {
toast({
variant: 'destructive',
Expand All @@ -94,6 +99,33 @@ export const InvoiceListPure: React.FC<InvoiceListPureProps> = ({ locale, access
}
};

const handleBulkDownload = async (selectedInvoiceIds: string[]) => {
if (selectedInvoiceIds.length === 0) return;

startTransition(async () => {
try {
for (const invoiceId of selectedInvoiceIds) {
try {
const response = await sdk.blocks.getInvoicePdf(invoiceId, { 'x-locale': locale }, accessToken);
Utils.DownloadFile.downloadFile(
response,
data.downloadFileName?.replace('{id}', invoiceId) || `invoice-${invoiceId}.pdf`,
);
await new Promise((resolve) => setTimeout(resolve, 100));
} catch (error) {
console.error(`Failed to download invoice ${invoiceId}:`, error);
}
}
} catch (_error) {
toast({
variant: 'destructive',
title: labels.errors.requestError.title,
description: labels.errors.requestError.content,
});
}
});
};

// Define columns configuration outside JSX for better readability
const columns = data.table.data.columns.map((column) => {
switch (column.id) {
Expand Down Expand Up @@ -156,6 +188,25 @@ export const InvoiceListPure: React.FC<InvoiceListPureProps> = ({ locale, access
}
: undefined;

const bulkActions = component.downloadAllButtonLabel
? (selectedRowKeys: Set<string | number>) => {
const selectedIds = Array.from(selectedRowKeys).map(String);
return (
<Button size="sm" onClick={() => handleBulkDownload(selectedIds)} disabled={isPending}>
<Download className="mr-2 h-4 w-4" />
{component.downloadAllButtonLabel}
</Button>
);
}
: undefined;

const bulkActionsLabel = component.bulkActionsLabel
? (count: number) => {
const msg = new IntlMessageFormat(component.bulkActionsLabel!, currentLocale);
return String(msg.format({ count }));
}
: undefined;

return (
<div className="w-full">
{initialData.length > 0 ? (
Expand Down Expand Up @@ -195,6 +246,12 @@ export const InvoiceListPure: React.FC<InvoiceListPureProps> = ({ locale, access
columns={columns}
actions={actions}
cardHeaderSlots={data.cardHeaderSlots}
enableRowSelection={component.enableRowSelection}
selectedRows={selectedRows}
onSelectionChange={setSelectedRows}
getRowKey={(item) => item.id}
bulkActions={bulkActions}
bulkActionsLabel={bulkActionsLabel}
/>

{data.pagination && (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ export const NotificationListPure: React.FC<NotificationListPureProps> = ({
const [data, setData] = useState<Model.NotificationListBlock>(component);
const [filters, setFilters] = useState(initialFilters);
const [viewMode, setViewMode] = useState<'list' | 'grid'>(initialViewMode);
const [selectedRows, setSelectedRows] = useState<Set<string | number>>(new Set());
const [isPending, startTransition] = useTransition();

const handleFilter = (data: Partial<Request.GetNotificationListBlockQuery>) => {
Expand All @@ -62,6 +63,7 @@ export const NotificationListPure: React.FC<NotificationListPureProps> = ({

setFilters(newFilters);
setData(newData);
setSelectedRows(new Set());
} catch (_error) {
toast({
variant: 'destructive',
Expand All @@ -83,6 +85,7 @@ export const NotificationListPure: React.FC<NotificationListPureProps> = ({

setFilters(initialFilters);
setData(newData);
setSelectedRows(new Set());
} catch (_error) {
toast({
variant: 'destructive',
Expand Down Expand Up @@ -201,6 +204,10 @@ export const NotificationListPure: React.FC<NotificationListPureProps> = ({
columns={columns}
actions={actions}
cardHeaderSlots={data.cardHeaderSlots}
enableRowSelection={component.enableRowSelection}
selectedRows={selectedRows}
onSelectionChange={setSelectedRows}
getRowKey={(item) => item.id}
/>

{data.pagination && (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export interface NotificationListProps {
locale: string;
routing: ReturnType<typeof defineRouting>;
hasPriority?: boolean;
enableRowSelection?: boolean;
}

export type NotificationListPureProps = NotificationListProps & Model.NotificationListBlock;
Expand Down
7 changes: 7 additions & 0 deletions packages/blocks/order-list/src/frontend/OrderList.client.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ export const OrderListPure: React.FC<OrderListPureProps> = ({ locale, accessToke
const [data, setData] = useState<Model.OrderListBlock>(component);
const [filters, setFilters] = useState(initialFilters);
const [viewMode, setViewMode] = useState<'list' | 'grid'>(initialViewMode);
const [selectedRows, setSelectedRows] = useState<Set<string | number>>(new Set());

const [isPending, startTransition] = useTransition();

Expand All @@ -60,8 +61,10 @@ export const OrderListPure: React.FC<OrderListPureProps> = ({ locale, accessToke
try {
const newFilters = { ...filters, ...data };
const newData = await sdk.blocks.getOrderList(newFilters, { 'x-locale': locale }, accessToken);

setFilters(newFilters);
setData(newData);
setSelectedRows(new Set());
} catch (_error) {
toast({
variant: 'destructive',
Expand All @@ -78,6 +81,7 @@ export const OrderListPure: React.FC<OrderListPureProps> = ({ locale, accessToke
const newData = await sdk.blocks.getOrderList(initialFilters, { 'x-locale': locale }, accessToken);
setFilters(initialFilters);
setData(newData);
setSelectedRows(new Set());
} catch (_error) {
toast({
variant: 'destructive',
Expand Down Expand Up @@ -207,6 +211,9 @@ export const OrderListPure: React.FC<OrderListPureProps> = ({ locale, accessToke
columns={columns}
actions={actions}
cardHeaderSlots={data.cardHeaderSlots}
enableRowSelection={component.enableRowSelection}
selectedRows={selectedRows}
onSelectionChange={setSelectedRows}
getRowKey={(item) => item.id.value}
/>

Expand Down
1 change: 1 addition & 0 deletions packages/blocks/order-list/src/frontend/OrderList.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export interface OrderListProps {
locale: string;
routing: ReturnType<typeof defineRouting>;
hasPriority?: boolean;
enableRowSelection?: boolean;
}

export interface OrderListRendererProps extends Omit<OrderListProps, ''> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,15 @@ export const ProductListPure: React.FC<ProductListPureProps> = ({ locale, access
const [filters, setFilters] = useState(initialFilters);
const [isPending, startTransition] = useTransition();
const [viewMode, setViewMode] = useState<ViewMode>('grid');
const [selectedRows, setSelectedRows] = useState<Set<string | number>>(new Set());

const handleFilter = (data: Partial<typeof initialFilters>) => {
startTransition(async () => {
const newFilters = { ...filters, ...data };
const newData = await sdk.blocks.getProductList(newFilters, { 'x-locale': locale }, accessToken);
setFilters(newFilters);
setData(newData);
setSelectedRows(new Set());
});
};

Expand All @@ -50,6 +52,7 @@ export const ProductListPure: React.FC<ProductListPureProps> = ({ locale, access
const newData = await sdk.blocks.getProductList(initialFilters, { 'x-locale': locale }, accessToken);
setFilters(initialFilters);
setData(newData);
setSelectedRows(new Set());
});
};

Expand Down Expand Up @@ -162,6 +165,9 @@ export const ProductListPure: React.FC<ProductListPureProps> = ({ locale, access
columns={columns}
actions={actions}
getRowKey={(item) => item.id}
enableRowSelection={component.enableRowSelection}
selectedRows={selectedRows}
onSelectionChange={setSelectedRows}
/>
</div>
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export interface ProductListProps {
accessToken?: string;
locale: string;
routing: ReturnType<typeof defineRouting>;
enableRowSelection?: boolean;
}

export type ProductListPureProps = ProductListProps & Model.ProductListBlock;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ export const TicketListPure: React.FC<TicketListPureProps> = ({ locale, accessTo
const [data, setData] = useState<Model.TicketListBlock>(component);
const [filters, setFilters] = useState(initialFilters);
const [viewMode, setViewMode] = useState<'list' | 'grid'>(initialViewMode);
const [selectedRows, setSelectedRows] = useState<Set<string | number>>(new Set());

const [isPending, startTransition] = useTransition();

Expand All @@ -59,6 +60,7 @@ export const TicketListPure: React.FC<TicketListPureProps> = ({ locale, accessTo
const newData = await sdk.blocks.getTicketList(newFilters, { 'x-locale': locale }, accessToken);
setFilters(newFilters);
setData(newData);
setSelectedRows(new Set());
} catch (_error) {
toast({
variant: 'destructive',
Expand All @@ -75,6 +77,7 @@ export const TicketListPure: React.FC<TicketListPureProps> = ({ locale, accessTo
const newData = await sdk.blocks.getTicketList(initialFilters, { 'x-locale': locale }, accessToken);
setFilters(initialFilters);
setData(newData);
setSelectedRows(new Set());
} catch (_error) {
toast({
variant: 'destructive',
Expand Down Expand Up @@ -214,6 +217,10 @@ export const TicketListPure: React.FC<TicketListPureProps> = ({ locale, accessTo
columns={columns}
actions={actions}
cardHeaderSlots={data.cardHeaderSlots}
enableRowSelection={component.enableRowSelection}
selectedRows={selectedRows}
onSelectionChange={setSelectedRows}
getRowKey={(item) => item.id}
/>

{data.pagination && (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export interface TicketListProps {
routing: ReturnType<typeof defineRouting>;
hasPriority?: boolean;
isDraftModeEnabled?: boolean;
enableRowSelection?: boolean;
}

export type TicketListPureProps = TicketListProps & Model.TicketListBlock;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,7 @@ export class InvoiceListBlock extends Block.Block {
right?: string;
bottom?: string;
};
enableRowSelection?: boolean;
bulkActionsLabel?: string;
downloadAllButtonLabel?: string;
}
Loading
Loading