Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

9/3リリース #874

Merged
merged 15 commits into from
Sep 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ FinanSu.iml
finansu.env
finansu.local.env
api/tmp
.DS_Store

# cloudflare
web/**/*.json
Expand Down
22 changes: 19 additions & 3 deletions view/next-project/src/components/common/EditButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@ import { RiPencilFill } from 'react-icons/ri';
interface Props {
onClick?: () => void;
isDisabled?: boolean;
size?: 'S' | 'M' | 'L';
}

const EditButton: React.FC<Props> = (props) => {
const { onClick, isDisabled = false } = props;
const { onClick, isDisabled = false, size } = props;

const buttonClass = useMemo(() => {
if (isDisabled) {
Expand All @@ -17,17 +18,32 @@ const EditButton: React.FC<Props> = (props) => {
}
}, [isDisabled]);

const iconSize = (): { button: string; icon: string } => {
switch (size) {
case 'S':
return { button: '6', icon: '12' };
case 'M':
return { button: '12', icon: '20' };
case 'L':
return { button: '24', icon: '30' };
default:
return { button: '6', icon: '12' };
}
};

return (
<button
suppressHydrationWarning
className={`${buttonClass} flex h-6 w-6 min-w-0 items-center justify-center rounded-full p-0`}
className={`${buttonClass} flex h-${iconSize().button} w-${
iconSize().button
} min-w-0 items-center justify-center rounded-full p-0`}
disabled={isDisabled}
onClick={(e) => {
if (onClick) onClick();
e.stopPropagation();
}}
>
<RiPencilFill size={'15px'} color={'white'} />
<RiPencilFill size={`${iconSize().icon}px`} color={'white'} />
</button>
);
};
Expand Down
2 changes: 1 addition & 1 deletion view/next-project/src/components/common/Input/Input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ interface Props {

function Input(props: Props): JSX.Element {
const className =
'rounded-full border border-primary-1 py-2 px-4' +
'rounded-full border border-primary-1 py-2 px-4 w-full' +
(props.className ? ` ${props.className}` : '');
return (
<div>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import router from 'next/router';
import { useEffect, useState } from 'react';
import { Expense, PurchaseOrder, PurchaseReport } from '@/type/common';
import { useState } from 'react';
import { Expense, PurchaseOrder, PurchaseReportView } from '@/type/common';
import { put } from '@/utils/api/purchaseOrder';
import { get } from '@api/api_methods';
import {
CloseButton,
Input,
Expand All @@ -14,38 +13,13 @@ import {

export const DetailEditModal: React.FC<{
purchaseReportId: number;
purchaseReportViewItem: PurchaseReportView;
expenses: Expense[];
isOpen: boolean;
setIsOpen: () => void;
onOpenInitial: () => void;
}> = ({ purchaseReportId, setIsOpen, onOpenInitial }) => {
const [expenses, setExpenses] = useState<Expense[]>([]);
const [purchaseOrder, setPurchaseOrder] = useState<PurchaseOrder>({
id: 0,
deadline: '',
userID: 0,
expenseID: 0,
financeCheck: false,
});

useEffect(() => {
const fetchData = async () => {
try {
const purchaseReportRes: PurchaseReport = await get(
`${process.env.CSR_API_URI}/purchasereports/${purchaseReportId}`,
);
const purchaseOrderId = purchaseReportRes.purchaseOrderID;
const expensesRes: Expense[] = await get(`${process.env.CSR_API_URI}/expenses`);
const purchaseOrderRes: PurchaseOrder = await get(
`${process.env.CSR_API_URI}/purchaseorders/${purchaseOrderId}`,
);
setExpenses(expensesRes);
setPurchaseOrder(purchaseOrderRes);
} catch (error) {
console.error('Failed to fetch data:', error);
}
};
fetchData();
}, [purchaseReportId]);
}> = ({ purchaseReportViewItem, expenses, setIsOpen, onOpenInitial }) => {
const [formData, setFormData] = useState<PurchaseOrder>(purchaseReportViewItem.purchaseOrder);

const formatDate = (date: string) => {
const d = new Date(date);
Expand All @@ -57,15 +31,15 @@ export const DetailEditModal: React.FC<{

const submit = async () => {
try {
const updatePurchaseOrderUrl = `${process.env.CSR_API_URI}/purchaseorders/${purchaseOrder.id}`;
await put(updatePurchaseOrderUrl, purchaseOrder);
const updatePurchaseOrderUrl = `${process.env.CSR_API_URI}/purchaseorders/${formData.id}`;
await put(updatePurchaseOrderUrl, formData);
} finally {
router.reload();
}
};

const handleInputChange = (key: keyof PurchaseOrder, value: string | number) => {
setPurchaseOrder((prev) => ({ ...prev, [key]: value }));
setFormData((prev) => ({ ...prev, [key]: value }));
};

return (
Expand All @@ -80,7 +54,7 @@ export const DetailEditModal: React.FC<{
<p className='text-lg text-black-600'>購入した局</p>
<div className='col-span-3 w-full'>
<Select
value={purchaseOrder.expenseID}
value={formData.expenseID}
onChange={(e) => handleInputChange('expenseID', Number(e.target.value))}
>
{expenses.map((data) => (
Expand All @@ -95,7 +69,7 @@ export const DetailEditModal: React.FC<{
<div className='col-span-3 w-full'>
<Input
type='date'
value={purchaseOrder.deadline ? formatDate(purchaseOrder.deadline) : ''}
value={formData.deadline ? formatDate(formData.deadline) : ''}
onChange={(e) => handleInputChange('deadline', e.target.value)}
className='w-full'
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,14 @@ import * as React from 'react';
import { useState } from 'react';

import { DetailEditModal } from './DetailEditModal';
import { Expense, PurchaseReportView } from '@/type/common';
import { CloseButton, EditButton, Modal, PrimaryButton } from '@components/common';
import EditModal from '@components/purchasereports/EditModal';

interface Props {
children?: React.ReactNode;
purchaseReportViewItem: PurchaseReportView;
expenses: Expense[];
id: number;
isDisabled: boolean;
}
Expand Down Expand Up @@ -47,6 +50,8 @@ const OpenEditModalButton: React.FC<Props> = (props) => {
{step === 'editDetails' && (
<DetailEditModal
purchaseReportId={props.id}
expenses={props.expenses}
purchaseReportViewItem={props.purchaseReportViewItem}
isOpen={true}
setIsOpen={closeModal}
onOpenInitial={onOpenInitial}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,11 @@ const UplaodFileModal: FC<ModalProps> = (props) => {
});
};

const generateRandomString = (charCount = 7): string => {
const str = Math.random().toString(36).substring(2).slice(-charCount);
return str.length < charCount ? str + 'a'.repeat(charCount - str.length) : str;
};

const submit = async () => {
if (!imageFile) {
return;
Expand All @@ -65,9 +70,24 @@ const UplaodFileModal: FC<ModalProps> = (props) => {

setIsLoading(true);
const formData = new FormData();

//拡張子取得
const uploadFileName = imageFile?.name || '';
const Extension = uploadFileName.split('.').pop();

// 日付取得
const date = new Date();
const thisMonth = date.getMonth() + 1;
const month = thisMonth < 10 ? '0' + thisMonth : thisMonth;
const day = date.getDate() < 10 ? '0' + date.getDate() : date.getDate();
const formattedDate = `${date.getFullYear()}${month}${day}`;
const randomStr = generateRandomString();

// ファイル名作成
const fileName = `receipt_${formattedDate}_${randomStr}.${Extension}`;

formData.append('file', imageFile);
const fileName = imageFile?.name || '';
formData.append('fileName', `receipts/${fileName}`);
formData.append('fileName', fileName);
formData.append('year', year);

const response = await fetch('/api/receipts', {
Expand All @@ -94,7 +114,7 @@ const UplaodFileModal: FC<ModalProps> = (props) => {
const sendReceipt: Receipt = {
purchaseReportID: Number(id),
bucketName: process.env.NEXT_PUBLIC_BUCKET_NAME || '',
fileName: imageFile.name,
fileName: fileName,
fileType: imageFile.type,
remark: '',
};
Expand Down Expand Up @@ -176,7 +196,7 @@ const UplaodFileModal: FC<ModalProps> = (props) => {
</div>
</div>
<div className='my-2 flex w-full flex-wrap justify-center'>
<PrimaryButton type='button' onClick={() => submit()} disabled={isLoading && !imageFile}>
<PrimaryButton type='button' onClick={() => submit()} disabled={isLoading || !imageFile}>
登録
</PrimaryButton>
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
import React, { useState } from 'react';

import { OpenEditInvoiceModalButton } from './index';
import { createSponsorActivitiesPDF } from '@/utils/createSponsorActivitiesInvoicesPDF';
import { PreviewPDF } from '@/utils/createSponsorActivitiesInvoicesPDF';
import { CloseButton, Input, Modal, PrimaryButton } from '@components/common';
import { SponsorActivityView } from '@type/common';
import {
SponsorActivityView,
Invoice,
SponsorStyleDetail,
InvoiceSponsorStyle,
} from '@type/common';

interface ModalProps {
setIsOpen: (isOpen: boolean) => void;
Expand All @@ -17,6 +23,7 @@ interface FormDateFormat {
}

export default function AddPdfDetailModal(props: ModalProps) {
const { sponsorActivitiesViewItem } = props;
const today = new Date();
const yyyy = String(today.getFullYear());
const mm = '08';
Expand Down Expand Up @@ -47,80 +54,100 @@ export default function AddPdfDetailModal(props: ModalProps) {
).padStart(2, '0')}`;
};

const [formData, setFormData] = useState<FormDateFormat>({
receivedAt: ymd,
billIssuedAt: todayFormatted(),
const sponsorStyleFormatted = (): InvoiceSponsorStyle[] => {
return sponsorActivitiesViewItem.styleDetail.map((sponsorStyleDetail) => {
const sponsorStyle = sponsorStyleDetail.sponsorStyle;
const res: InvoiceSponsorStyle = {
styleName: `${sponsorStyle.style}(${sponsorStyle.feature})`,
price: sponsorStyle.price,
};
return res;
});
};

const CalculateTotalPrice = () => {
return sponsorActivitiesViewItem.styleDetail.reduce(
(price: number, sponsorStyleDetail: SponsorStyleDetail): number => {
return price + sponsorStyleDetail.sponsorStyle.price;
},
0,
);
};

const [invoiceData, setInvoiceDate] = useState<Invoice>({
sponsorName: sponsorActivitiesViewItem.sponsor.name,
managerName: sponsorActivitiesViewItem.sponsor.representative,
totalPrice: CalculateTotalPrice(),
fesStuffName: sponsorActivitiesViewItem.user.name,
invoiceSponsorStyle: sponsorStyleFormatted(),
issuedDate: todayFormatted(),
deadline: ymd,
remark: '',
});
const [remarks, setRemarks] = useState('');

const handler =
(input: string) =>
(e: React.ChangeEvent<HTMLSelectElement> | React.ChangeEvent<HTMLInputElement>) => {
setFormData({ ...formData, [input]: e.target.value });
setInvoiceDate({ ...invoiceData, [input]: e.target.value });
};

const handleRemarksChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setRemarks(e.target.value);
const onClose = () => {
props.setIsOpen(false);
};

return (
<Modal className='md:w-1/2'>
<Modal className='md:w-1/2' onClick={onClose}>
<div className='w-full'>
<div className='ml-auto w-fit'>
<CloseButton
onClick={() => {
props.setIsOpen(false);
}}
/>
<CloseButton onClick={onClose} />
</div>
<p className='mx-auto mb-7 w-fit text-2xl font-thin leading-8 tracking-widest text-black-600'>
振込締め切り日・備考の入力
請求書の発行
</p>
<div className='col-span-4 w-full'>
<p className='text-gray-600 mb-3 ml-1 text-sm'>請求書発行日</p>
<Input
type='date'
value={formData.billIssuedAt}
onChange={handler('billIssuedAt')}
value={invoiceData.issuedDate}
onChange={handler('issuedDate')}
className='mb-3 w-full'
/>
<p className='text-gray-600 mb-3 ml-1 text-sm'>振込締め切り日</p>
<Input
type='date'
value={formData.receivedAt}
onChange={handler('receivedAt')}
value={invoiceData.deadline}
onChange={handler('deadline')}
className='mb-3 w-full'
/>
<p className='text-gray-600 mb-3 ml-1 text-sm'>備考を入力</p>
<Input
type='text'
value={remarks}
onChange={handleRemarksChange}
value={invoiceData.remark}
onChange={handler('remark')}
className='mb-3 w-full'
/>
</div>
<div className='mb-3 flex w-full justify-center'>
<div className='mb-3 flex w-full justify-center gap-4'>
<PrimaryButton
onClick={async () => {
createSponsorActivitiesPDF(
props.sponsorActivitiesViewItem,
formatDate(formData.receivedAt),
formatDate(formData.billIssuedAt, false),
remarks,
invoiceData,
formatDate(invoiceData.deadline),
formatDate(invoiceData.issuedDate, false),
);
props.setIsOpen(false);
onClose();
}}
>
ダウンロード
</PrimaryButton>
<OpenEditInvoiceModalButton invoice={invoiceData} setInvoice={setInvoiceDate} />
</div>
</div>
<div className='h-[30rem] justify-center overflow-x-auto md:flex'>
<PreviewPDF
sponsorActivitiesViewItem={props.sponsorActivitiesViewItem}
date={formatDate(formData.receivedAt)}
issuedDate={formatDate(formData.billIssuedAt, false)}
remarks={remarks}
invoiceItem={invoiceData}
deadline={formatDate(invoiceData.deadline)}
issuedDate={formatDate(invoiceData.issuedDate, false)}
/>
</div>
</Modal>
Expand Down
Loading
Loading