Skip to content

Instantly share code, notes, and snippets.

@AlexandroMtzG
Last active February 6, 2023 05:05
Show Gist options
  • Save AlexandroMtzG/2379ac2a52583612bb6d95862d25822e to your computer and use it in GitHub Desktop.
Save AlexandroMtzG/2379ac2a52583612bb6d95862d25822e to your computer and use it in GitHub Desktop.
// Component: Form with creating, reading, updating, and deleting states
// Date: 2023-02-04
// Version: SaasRock v0.8.2
import { useTransition, useSubmit, Form, useSearchParams } from "@remix-run/react";
import { t } from "i18next";
import { useEffect, useRef, useState } from "react";
import ButtonPrimary from "~/components/ui/buttons/ButtonPrimary";
import ButtonSecondary from "~/components/ui/buttons/ButtonSecondary";
import InputGroup from "~/components/ui/forms/InputGroup";
import ActionResultModal from "~/components/ui/modals/ActionResultModal";
import ConfirmModal, { RefConfirmModal } from "~/components/ui/modals/ConfirmModal";
import { DocumentDto } from "../dtos/DocumentDto";
import InputMedia from "~/components/ui/input/InputMedia";
import { useAppData } from "~/utils/data/useAppData";
import InputSelector from "~/components/ui/input/InputSelector";
import DocumentTypeHelper from "~/modules/documents/helpers/DocumentTypeHelper";
import { DocumentTypeDto } from "../../document-types/dtos/DocumentTypeDto";
export default function DocumentForm({
item,
actionData,
isUpdating,
isCreating,
canUpdate,
canDelete,
onCancel,
}: {
item?: DocumentDto;
actionData: { success?: string; error?: string } | undefined;
isUpdating?: boolean;
isCreating?: boolean;
canUpdate?: boolean;
canDelete?: boolean;
onCancel?: () => void;
}) {
const appData = useAppData();
const transition = useTransition();
const submit = useSubmit();
const [searchParams] = useSearchParams();
const [selectedType, setSelectedType] = useState<DocumentTypeDto | undefined>();
const [typeId, setTypeId] = useState<string | undefined>();
const [year, setYear] = useState<number | undefined>(new Date().getFullYear());
const [period, setPeriod] = useState<number | undefined>(1);
useEffect(() => {
if (item) {
// Existing Values
setTypeId(item.typeId);
setYear(item.year);
setPeriod(item.period);
} else {
// Default Values
if (searchParams.get("typeId")) {
setTypeId(searchParams.get("typeId")?.toString());
} else {
if (isCreating && appData.documentTypes.length > 0) {
setTypeId(appData.documentTypes[0].row.id);
}
}
if (searchParams.get("year")) {
setYear(Number(searchParams.get("year")));
} else {
setYear(new Date().getFullYear());
}
if (searchParams.get("period")) {
setPeriod(Number(searchParams.get("period")));
} else {
setPeriod(1);
}
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
useEffect(() => {
setSelectedType(appData.documentTypes.find((x) => x.row.id === typeId));
}, [appData.documentTypes, typeId]);
const confirmDelete = useRef<RefConfirmModal>(null);
function onDelete() {
confirmDelete.current?.show(t("shared.confirmDelete"), t("shared.delete"), t("shared.cancel"), t("shared.warningCannotUndo"));
}
function onDeleteConfirmed() {
const form = new FormData();
form.set("action", "delete");
submit(form, {
method: "post",
});
}
function isDisabled() {
if (isUpdating && !canUpdate) {
return true;
}
if (!isUpdating && !isCreating) {
return true;
}
}
function getPeriods() {
const selectedType = appData.documentTypes.find((x) => x.row.id === typeId);
if (!selectedType) {
return [];
}
const periods = DocumentTypeHelper.getDocumentTypePeriods({ type: selectedType, year: year ?? new Date().getFullYear() });
return periods.map((f) => {
return {
value: f.number,
name: f.name,
};
});
}
return (
<Form key={!isDisabled() ? "enabled" : "disabled"} method="post" className="space-y-4">
{item ? <input name="action" value="edit" hidden readOnly /> : <input name="action" value="create" hidden readOnly />}
<InputGroup title={t("shared.details")}>
<div className="space-y-2">
<InputSelector
name="typeId"
title={t("Type")}
required
autoFocus
disabled={isDisabled()}
value={typeId}
setValue={(e) => setTypeId(e?.toString())}
options={appData.documentTypes.map((x) => ({ value: x.row.id, name: x.name }))}
withSearch={false}
/>
<InputSelector
name="year"
title={t("Year")}
disabled={isDisabled() || !selectedType?.timesInYear}
value={year}
setValue={(e) => setYear(Number(e))}
options={DocumentTypeHelper.getPossibleYears()}
withSearch={false}
/>
<InputSelector
name="period"
title={t("Period")}
disabled={isDisabled() || !selectedType?.timesInYear}
value={period}
setValue={(e) => setPeriod(Number(e))}
options={getPeriods()}
/>
<InputMedia
name="document"
title={t("Document")}
required
disabled={isDisabled()}
initialMedia={item?.document ? [item?.document] : []}
min={1}
max={1}
accept=".pdf"
maxSize={20}
/>
</div>
</InputGroup>
{(isCreating || (isUpdating && canUpdate)) && (
<div className="flex justify-between space-x-2">
<div>
{canDelete && (
<ButtonSecondary disabled={transition.state !== "idle"} destructive onClick={onDelete}>
{t("shared.delete")}
</ButtonSecondary>
)}
</div>
<div className="flex space-x-2">
{onCancel && <ButtonSecondary onClick={onCancel}>{t("shared.cancel")}</ButtonSecondary>}
<ButtonPrimary disabled={transition.state !== "idle"} type="submit">
{item ? t("shared.save") : t("shared.create")}
</ButtonPrimary>
</div>
</div>
)}
<ConfirmModal ref={confirmDelete} onYes={onDeleteConfirmed} />
<ActionResultModal actionData={actionData} showSuccess={false} />
</Form>
);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment