Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
eda29a3
[add] Add buy reports summary endpoint and related types
nakatashingo Dec 28, 2025
3fa14ff
[add] Implement PurchaseReportSummaryAmounts component and integrate …
nakatashingo Dec 28, 2025
9d47aeb
[otr] Simplify JSX formatting in PurchaseReportSummaryAmounts component
nakatashingo Dec 28, 2025
44167c1
[add] PurchaseReportPaidByFilterModal component and integrate it into…
nakatashingo Dec 31, 2025
3b6ec0a
[fix] Update button text in PurchaseReportPaidByFilterModal
nakatashingo Dec 31, 2025
3c61495
[fix] Update button text in PurchaseReportPaidByFilterModal to improv…
nakatashingo Dec 31, 2025
dff5edd
[add] Implement PurchaseReportSummaryAmounts component and integrate …
nakatashingo Dec 28, 2025
72f01f1
[otr] Simplify JSX formatting in PurchaseReportSummaryAmounts component
nakatashingo Dec 28, 2025
95d6546
[add] PurchaseReportPaidByFilterModal component and integrate it into…
nakatashingo Dec 31, 2025
2c7c7b7
[fix] Update button text in PurchaseReportPaidByFilterModal
nakatashingo Dec 31, 2025
2894b01
[fix] Update button text in PurchaseReportPaidByFilterModal to improv…
nakatashingo Dec 31, 2025
d88ee44
[add] Add "絞り込みなし" option to bureau selection in PurchaseReportPaidBy…
nakatashingo Jan 24, 2026
96fdbdb
refactor: Reorganize imports and clean up whitespace in PurchaseRepor…
nakatashingo Jan 24, 2026
29d8823
formatted by workflow
nakatashingo Jan 24, 2026
ffe2764
[add] Insert additional payment receipt entries in initial schema seed
nakatashingo Jan 30, 2026
1b78882
[fix] Improve styling and structure in PurchaseReportPaidByFilterModa…
nakatashingo Jan 30, 2026
b7cda1c
[fix] Simplify user filtering logic and improve type checking in Purc…
nakatashingo Jan 31, 2026
9dbe13b
[fix] Update PurchaseReportPaidByFilterModal to use paidBy instead of…
nakatashingo Feb 4, 2026
566c3a9
[fix] Enhance PurchaseReportPaidByFilterModal and PurchaseReports to …
nakatashingo Feb 12, 2026
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: 5 additions & 1 deletion mysql/seed/000001_initial_schema_seed.sql
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,11 @@ INSERT INTO
VALUES
(1, 'payment-receipts', 'receipt-1.jpg', 'image/jpeg', ''),
(2, 'payment-receipts', 'receipt-2.jpg', 'image/jpeg', ''),
(3, 'payment-receipts', 'receipt-3.jpg', 'image/jpeg', '');
(3, 'payment-receipts', 'receipt-3.jpg', 'image/jpeg', ''),
(4, 'payment-receipts', 'receipt-4.jpg', 'image/jpeg', ''),
(5, 'payment-receipts', 'receipt-5.jpg', 'image/jpeg', ''),
(6, 'payment-receipts', 'receipt-6.jpg', 'image/jpeg', ''),
(7, 'payment-receipts', 'receipt-7.jpg', 'image/jpeg', '');

INSERT INTO
buy_statuses (buy_report_id, is_packed, is_settled)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
import React, { FC, useEffect, useMemo, useState } from 'react';

import { CloseButton, Modal, OutlinePrimaryButton, Select } from '@components/common';
import { Bureau, User } from '@type/common';
import { normalizePaidBy } from '@/utils/purchaseReportFilters';

interface PurchaseReportPaidByFilterModalProps {
isOpen: boolean;
onClose: () => void;
onApply: (selection: {
bureauId: number | null;
paidByUserId: number | null;
paidBy: string | null | undefined;
}) => void;
bureaus: Bureau[];
users: User[];
selectedBureauId: number | null;
selectedPaidByUserId: number | null;
selectedPaidBy: string | null | undefined;
}

const PurchaseReportPaidByFilterModal: FC<PurchaseReportPaidByFilterModalProps> = (props) => {
const {
isOpen,
onClose,
onApply,
bureaus,
users,
selectedBureauId,
selectedPaidByUserId,
selectedPaidBy,
} = props;

const [draftBureauId, setDraftBureauId] = useState<number | null>(selectedBureauId);
const [draftPaidByUserId, setDraftPaidByUserId] = useState<number | null>(
selectedPaidByUserId ?? null,
);
const [draftPaidBy, setDraftPaidBy] = useState<string | null>(normalizePaidBy(selectedPaidBy));

useEffect(() => {
if (!isOpen) return;
setDraftBureauId(selectedBureauId);
setDraftPaidByUserId(selectedPaidByUserId ?? null);
setDraftPaidBy(normalizePaidBy(selectedPaidBy));
}, [isOpen, selectedBureauId, selectedPaidBy, selectedPaidByUserId]);

const labelClassName = 'mb-2 text-sm text-black-600 [font-family:"Noto_Sans_JP"]';
const selectTextClassName = 'text-black-600 [font-family:"Noto_Sans_JP"]';
const optionClassName = 'text-black-600 [font-family:"Noto_Sans_JP"]';

const bureauNameMap = useMemo(
() =>
new Map(
bureaus.map((bureau) => [bureau.id ?? 0, bureau.name] as const).filter(([id]) => id > 0),
),
[bureaus],
);

const filteredUsers = useMemo(() => {
if (!draftBureauId) return users;
return users.filter((user) => user.bureauID === draftBureauId);
}, [draftBureauId, users]);

const legacyPaidBy = draftPaidByUserId == null ? draftPaidBy : null;
const legacyPaidByValue = legacyPaidBy ? `legacy:${legacyPaidBy}` : '';
const paidBySelectValue =
draftPaidByUserId != null ? String(draftPaidByUserId) : legacyPaidByValue;

const handleBureauChange = (event: React.ChangeEvent<HTMLSelectElement>) => {
const value = event.target.value;
const nextBureauId = value === '' ? null : Number(value);
setDraftBureauId(nextBureauId);
setDraftPaidByUserId(null);
setDraftPaidBy(null);
};

const handlePaidByChange = (event: React.ChangeEvent<HTMLSelectElement>) => {
const value = event.target.value;
if (value === '') {
setDraftPaidByUserId(null);
setDraftPaidBy(null);
return;
}

if (value.startsWith('legacy:')) {
setDraftPaidByUserId(null);
setDraftPaidBy(normalizePaidBy(value.replace('legacy:', '')));
return;
}

const nextUserId = Number(value);
if (!Number.isFinite(nextUserId) || nextUserId <= 0) {
setDraftPaidByUserId(null);
setDraftPaidBy(null);
return;
}

const selectedUser = users.find((user) => user.id === nextUserId);
setDraftPaidByUserId(nextUserId);
setDraftPaidBy(normalizePaidBy(selectedUser?.name ?? null));
};

const handleApply = () => {
onApply({
bureauId: draftBureauId ?? null,
paidByUserId: draftPaidByUserId ?? null,
paidBy: normalizePaidBy(draftPaidBy),
});
};

return (
<Modal className='w-[90vw] max-w-[440px] p-6 shadow-lg' onClick={onClose}>
<div className='flex justify-end'>
<CloseButton onClick={onClose} />
</div>
<div className='mt-2 space-y-5'>
<div>
<p className={labelClassName}>局名</p>
<Select
className={selectTextClassName}
value={draftBureauId ?? ''}
onChange={handleBureauChange}
>
<option className={optionClassName} value=''>
絞り込みなし
</option>
{bureaus.map((bureau) => (
<option className={optionClassName} key={bureau.id ?? 0} value={bureau.id ?? 0}>
{bureau.name}
</option>
))}
</Select>
</div>
<div>
<p className={labelClassName}>氏名</p>
<Select
className={selectTextClassName}
value={paidBySelectValue}
onChange={handlePaidByChange}
>
<option className={optionClassName} value=''>
絞り込みなし
</option>
{legacyPaidBy && (
<option className={optionClassName} value={legacyPaidByValue}>
{legacyPaidBy}
</option>
)}
{filteredUsers.map((user) => {
const bureauName = bureauNameMap.get(user.bureauID);
const label = draftBureauId || !bureauName ? user.name : `${bureauName} ${user.name}`;
return (
<option className={optionClassName} key={user.id} value={user.id}>
{label}
</option>
);
})}
</Select>
</div>
</div>
<div className='mt-6 flex justify-center'>
<OutlinePrimaryButton onClick={handleApply}>絞り込む</OutlinePrimaryButton>
</div>
</Modal>
);
};

export default PurchaseReportPaidByFilterModal;
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
interface PurchaseReportSummaryAmountsProps {
unsettledAmountText: string;
unpackedAmountText: string;
className?: string;
}

export default function PurchaseReportSummaryAmounts({
unsettledAmountText,
unpackedAmountText,
className = '',
}: PurchaseReportSummaryAmountsProps) {
return (
<div className={className}>
<div className='inline-grid grid-cols-[auto_auto_auto] gap-1 text-sm font-medium leading-normal text-[#666] [font-family:"Noto_Sans_JP"]'>
<span className='whitespace-nowrap'>未清算金額</span>
<span className='whitespace-nowrap'>:</span>
<span className='min-w-[12ch] whitespace-nowrap text-right'>
{unsettledAmountText}
{'\u00A0\u00A0'}円
</span>

<span className='whitespace-nowrap'>未封詰め金額</span>
<span className='whitespace-nowrap'>:</span>
<span className='min-w-[12ch] whitespace-nowrap text-right'>
{unpackedAmountText}
{'\u00A0\u00A0'}円
</span>
</div>
</div>
);
}
1 change: 1 addition & 0 deletions view/next-project/src/components/purchasereports/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,6 @@ export { default as PurchaseOrderListModal } from './PurchaseOrderListModal';
export { default as PurchaseReportAddModal } from './PurchaseReportAddModal';
export { default as PurchaseReportConfirmModal } from './PurchaseReportConfirmModal';
export { default as PurchaseReportItemNumModal } from './PurchaseReportItemNumModal'; // "PurchaseReport|temNumModal"を修正しました。
export { default as PurchaseReportSummaryAmounts } from './PurchaseReportSummaryAmounts';
export { default as ReceiptModal } from './ReceiptModal';
export { default as CheckSettlementConfirmModal } from './CheckSettlementConfirmModal';
Loading