Skip to content
Open
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
15 changes: 7 additions & 8 deletions src/components/form/FileUpload.tsx
Original file line number Diff line number Diff line change
@@ -1,31 +1,30 @@
import React, { useState, ChangeEvent, DragEvent } from "react";

interface FileUploadProps {
files: File[];
onFileChange: (files: File[]) => void;
multiple?: boolean;
}

const FileUpload = (props: FileUploadProps) => {
const [files, setFiles] = useState<File[]>([]);

const FileUpload = ({ files, onFileChange, multiple = true }: FileUploadProps) => {
const handleFileChange = (event: ChangeEvent<HTMLInputElement>) => {
const newFiles = Array.from(event.target.files || []);
props.onFileChange([...files, ...newFiles]);
setFiles((prevFiles) => [...prevFiles, ...newFiles]);
onFileChange([...files, ...newFiles]);
event.target.value = "";
};

const handleDrop = (event: DragEvent<HTMLDivElement>) => {
event.preventDefault();
const newFiles = Array.from(event.dataTransfer.files);
setFiles((prevFiles) => [...prevFiles, ...newFiles]);
onFileChange([...files, ...newFiles]);
};

const handleDragOver = (event: DragEvent<HTMLDivElement>) => {
event.preventDefault();
};

const removeFile = (index: number) => {
props.onFileChange(files.filter((_, i) => i !== index));
setFiles((prevFiles) => prevFiles.filter((_, i) => i !== index));
onFileChange(files.filter((_, i) => i !== index));
};

return (
Expand Down
181 changes: 154 additions & 27 deletions src/pages/ReceiptPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,12 @@ const ReceiptPage = () => {

const [usedOnlineCard, setUsedOnlineCard] = useState(false);
const [disableSubmit, setDisableSubmit] = useState(false);
const [amountInput, setAmountInput] = useState("");
const [accountNumber, setAccountNumber] = useState("");
const [cardNumber, setCardNumber] = useState("");

const [attachments, setAttachments] = useState<File[]>([]);
const allowedTypes = ["application/pdf", "image/png", "image/jpeg", "image/jpg"];

const auth = useAuth();
const { user } = auth;
Expand All @@ -63,7 +67,16 @@ const ReceiptPage = () => {
});

const onFileChange = async (files: File[]) => {
setAttachments([...files]);
const validFiles = files.filter((file) => allowedTypes.includes(file.type));
const invalidFiles = files.filter((file) => !allowedTypes.includes(file.type));

if (invalidFiles.length > 0) {
alert("Bare PDF eller bildefiler (JPG, PNG, JPEG) er tillatt. Ugyldige filer ble ignorert.");
}

if (validFiles.length > 0) {
setAttachments(validFiles);
}
};

const [formdata, setFormdata]: [FormData, any] = useState({
Expand All @@ -76,8 +89,90 @@ const ReceiptPage = () => {
account_number: "",
});

const [errors, setErrors] = useState({
amount: "",
account_number: "",
card_number: "",
name: "",
committee_id: "",
attachments: "",
});

const validateForm = () => {
const newErrors: typeof errors = {
amount: "",
account_number: "",
card_number: "",
name: "",
committee_id: "",
attachments: "",
};

if (!amountInput) {
newErrors.amount = "Beløp må fylles inn";
} else {
const num = Number(amountInput);
if (num <= 0) {
newErrors.amount = "Beløp må være større enn 0";
} else if (num > 100000) {
newErrors.amount = "Beløp kan ikke overstige 100 000";
}
}

if (!usedOnlineCard) {
if (!/^\d{11}$/.test(formdata.account_number || "")) {
newErrors.account_number = "Kontonummer må være 11 sifre";
}
}

if (usedOnlineCard) {
const cardNumber = formdata.card_number || "";
if (!/^\d{16}$/.test(cardNumber)) {
newErrors.card_number = "Kortnummer må være 16 sifre";
}
}

if (formdata.name.trim() === "") {
newErrors.name = "Vennligst skriv anledning";
}

if (!formdata.committee_id) {
newErrors.committee_id = "Velg en ansvarlig enhet";
}

if (attachments.length === 0) {
newErrors.attachments = "Last opp minst én kvittering/vedlegg";
}

setErrors(newErrors);

return Object.values(newErrors).every((e) => e === "");
};

const formatAccountNumber = (value: string) => {
const digits = value.replace(/\D/g, "");
const parts: string[] = [];

if (digits.length > 0) parts.push(digits.substring(0, 4));
if (digits.length > 4) parts.push(digits.substring(4, 6));
if (digits.length > 6) parts.push(digits.substring(6, 11));

return parts.join(" ");
};

const formatCardNumber = (value: string) => {
return value
.replace(/\D/g, "")
.replace(/(.{4})/g, "$1 ")
.trim();
};

const submitform = async () => {
if (!validateForm()) return;

const numericAmount = parseFloat(amountInput);
const updatedFormData = { ...formdata, amount: numericAmount };

setDisableSubmit(true);
const paymentInfo: PaymentInformation = {
usedOnlineCard: usedOnlineCard,
Expand All @@ -92,22 +187,20 @@ const ReceiptPage = () => {
id: 0,
};
const body: ReceiptRequestBody = {
receipt: formdata,
receipt: updatedFormData,
attachments: await Promise.all(
[...attachments].map(async (file) => await fileToBase64(file)),
),
receiptPaymentInformation: paymentInfo,
};

try {
await submitReceipt(body);
alert("Kvittering sendt inn!");
// TODO: Fix with popup success message in home something
navigate("/?receiptsubmittedsuccess=1");
await submitReceipt(body);
alert("Kvittering sendt inn!");
// TODO: Fix with popup success message in home something
navigate("/?receiptsubmittedsuccess=1");
} catch (e) {

alert("Noe gikk galt, prøv igjen senere");

}

setDisableSubmit(false);
Expand Down Expand Up @@ -161,25 +254,33 @@ const ReceiptPage = () => {
<div className="flex-col w-[20rem]">
<p className="text-left tracking-wide">Kontonummer</p>
<input
type="text"
placeholder={"2345 XX XXXX"}
className="text-black p-3 rounded w-full"
value={formatAccountNumber(accountNumber)}
maxLength={13}
onChange={(e) => {
setFormdata({ ...formdata, account_number: e.target.value });
const raw = e.target.value.replace(/\D/g, "");
setAccountNumber(raw);
setFormdata({ ...formdata, account_number: raw });
}}
></input>
<p className="text-red-500 text-sm min-h-[1.25rem]">
{errors.account_number || " "}
</p>
</div>
<div className="flex-col w-[20rem]">
<p className="text-left tracking-wide">Beløp</p>
<input
type="number"
placeholder={"530"}
className="text-black p-3 rounded w-full"
onChange={(e) => {
setFormdata({
...formdata,
amount: parseInt(e.target.value),
});
}}
max={100000}
onChange={(e) => setAmountInput(e.target.value)}
></input>
<p className="text-red-500 text-sm min-h-[1.25rem]">
{errors.amount || " "}
</p>
</div>
</div>
<div className="flex justify-center mt-[10px] gap-3 flex-col md:gap-10 md:flex-row items-center">
Expand All @@ -192,6 +293,9 @@ const ReceiptPage = () => {
setFormdata({ ...formdata, name: e.target.value });
}}
></input>
<p className="text-red-500 text-sm min-h-[1.25rem]">
{errors.name || " "}
</p>
</div>
<div className="flex-col w-[20rem]">
<p className="text-left tracking-wide">Ansvarlig enhet</p>
Expand All @@ -215,33 +319,44 @@ const ReceiptPage = () => {
})
: null}
</select>
<p className="text-red-500 text-sm min-h-[1.25rem]">
{errors.committee_id || " "}
</p>
</div>
</div>
</div>
<div className={`${!usedOnlineCard ? "hidden" : ""} text-white`}>
<div className="flex justify-center gap-3 flex-col md:gap-10 md:flex-row items-center">
<div className="flex-col w-[20rem]">
<p className="text-left tracking-wide">Kortinformasjon</p>
<p className="text-left tracking-wide">Kortnummer</p>
<input
placeholder={"Kortnummer/Hvilken komite korter tilhører"}
type="text"
placeholder={"2345 XXXX XXXX XXXX"}
className="text-black p-3 rounded w-full"
value={formatCardNumber(cardNumber)}
maxLength={19}
onChange={(e) => {
setFormdata({ ...formdata, card_number: e.target.value });
const raw = e.target.value.replace(/\D/g, "");
setCardNumber(raw);
setFormdata({ ...formdata, card_number: raw });
}}
></input>
/>
<p className="text-red-500 text-sm min-h-[1.25rem]">
{errors.card_number || " "}
</p>
</div>
<div className="flex-col w-[20rem]">
<p className="text-left tracking-wide">Beløp</p>
<input
type="number"
placeholder={"530"}
className="text-black p-3 rounded w-full"
onChange={(e) => {
setFormdata({
...formdata,
amount: parseInt(e.target.value),
});
}}
max={100000}
onChange={(e) => setAmountInput(e.target.value)}
></input>
<p className="text-red-500 text-sm min-h-[1.25rem]">
{errors.amount || " "}
</p>
</div>
</div>
<div className="flex justify-center mt-[10px] gap-3 flex-col md:gap-10 md:flex-row items-center">
Expand All @@ -254,6 +369,9 @@ const ReceiptPage = () => {
setFormdata({ ...formdata, name: e.target.value });
}}
></input>
<p className="text-red-500 text-sm min-h-[1.25rem]">
{errors.name || " "}
</p>
</div>
<div className="flex-col w-[20rem]">
<p className="text-left tracking-wide">Ansvarlig enhet</p>
Expand All @@ -267,7 +385,7 @@ const ReceiptPage = () => {
});
}}
>
<option value="None">Ingen</option>
<option value="">Ingen</option>
{data && data.length
? data.map((committee: any) => {
return (
Expand All @@ -278,6 +396,9 @@ const ReceiptPage = () => {
})
: null}
</select>
<p className="text-red-500 text-sm min-h-[1.25rem]">
{errors.committee_id || " "}
</p>
</div>
</div>
</div>
Expand All @@ -296,7 +417,13 @@ const ReceiptPage = () => {
</div>
<div className="flex-col mx-5">
<p className="text-white w-full text-left text-l mb-[5px]">Vedlegg</p>
<FileUpload onFileChange={onFileChange} />
<FileUpload
files={attachments}
onFileChange={onFileChange}
/>
<p className="text-red-500 text-sm min-h-[1.25rem]">
{errors.attachments || " "}
</p>
</div>
<div className="flex-col mt-[20px] mx-5">
<p className="text-white w-full text-left text-l mb-[5px]">
Expand Down