From d5eff6095e91de117fd193e509515438ec7b2480 Mon Sep 17 00:00:00 2001 From: Katja Lutz Date: Wed, 22 Jun 2022 21:50:24 +0200 Subject: [PATCH] feat: implement settings Overlay component --- src/components/Settings/Overlay.tsx | 973 ++++++++++++++++++++++++++++ 1 file changed, 973 insertions(+) create mode 100644 src/components/Settings/Overlay.tsx diff --git a/src/components/Settings/Overlay.tsx b/src/components/Settings/Overlay.tsx new file mode 100644 index 0000000..23aa0c4 --- /dev/null +++ b/src/components/Settings/Overlay.tsx @@ -0,0 +1,973 @@ +import { + batch, + Component, + FlowComponent, + For, + Show, + useContext, + JSX, + startTransition, + createMemo, + onMount, + onCleanup, +} from "solid-js"; +import { createStore, reconcile, unwrap } from "solid-js/store"; +import { format, fromUnixTime } from "date-fns"; +import z from "myzod"; +import Big from "big.js"; +import { generate } from "node-iso11649"; +import { customAlphabet } from "nanoid"; + +import createAccordion from "../Accordion"; +import { Checkbox, TextArea, TextInput, UnixDateInput } from "../Form"; +import { autoAnimate } from "~/directives/autoAnimate"; +import { + LocalStoreContext, + localStoreSchema, + POSITION_TYPE_AGILE, + POSITION_TYPE_QUANTITY, + PRINT_TYPE_CONFIRMATION, + PRINT_TYPE_INVOICE, + PRINT_TYPE_OFFER, + StoreContext, + storeSchema, + UiStoreContext, +} from "~/stores"; +import { AddressData, isStructuredAddress } from "../Address"; +import PositionsIcon from "~icons/carbon/show-data-cards"; +import YouIcon from "~icons/carbon/face-wink"; +import DesignIcon from "~icons/carbon/paint-brush"; +import PrinterIcon from "~icons/carbon/printer"; +import ProjectIcon from "~icons/carbon/product"; +import DownloadIcon from "~icons/carbon/download"; +import LoadIcon from "~icons/carbon/folder"; +import LoadingSpinnerIcon from "~icons/icomoon-free/spinner9"; +import ErrorIcon from "~icons/carbon/error"; +import SuccessIcon from "~icons/carbon/checkmark-filled"; +import CustomerIcon from "~icons/carbon/friendship"; +import WarningIcon from "~icons/carbon/warning-alt-filled"; +import GenerateIcon from "~icons/carbon/chemistry"; + +import { saveFile, selectLocalFiles, uploadFile } from "~/client/filesystem"; +import { resetInput, sleep } from "~/util"; +import { PositionsSettings } from "./Positions"; +import Modal, { ModalCloseButton } from "../Modal"; +import { createValidation } from "~/hooks/validation"; +import { MarkdownHelpLabel } from "../Markdown"; + +const AccordionItemGrid: FlowComponent = (props) => { + return ( +
{props.children}
+ ); +}; + +const AccordionItemEnd: Component = () => { + return
; +}; + +const AccordionItemDivider: FlowComponent = (props) => { + return
{props.children}
; +}; + +const SettingsOverlay: Component = () => { + const [state, setState] = useContext(StoreContext)!; + const [localState, setLocalState] = useContext(LocalStoreContext)!; + const [loadModal, setLoadModal] = createStore({ + open: false, + loading: false, + errors: null as null | { + message?: JSX.Element; + parseErrors: { path: string; message: string }[]; + }, + }); + const [uiState, setUiState] = useContext(UiStoreContext)!; + + const [AccordionItem] = createAccordion(null); + + autoAnimate; + + const [DocumentValidationContext, documentDataForm] = createValidation(); + const [YourDataValidationContext, yourDataForm] = createValidation(); + const [CustomerValidationContext, customerDataForm] = createValidation(); + + const AddressInputs: Component<{ + namePrefix?: string; + nameRequired?: boolean; + setter: (name: string, value: any) => void; + address: () => AddressData; + }> = (props) => { + const isStructured = createMemo(() => isStructuredAddress(props.address())); + const withPrefix = (name: string) => + createMemo( + () => `${props.namePrefix && props.namePrefix + "_"}${name}` + )(); + + return ( + <> +
+ props.setter("name", evt.currentTarget.value)} + /> +
+ + props.setter("line1", evt.currentTarget.value)} + /> + props.setter("line2", evt.currentTarget.value)} + /> + + props.setter( + "zip", + parseInt(evt.currentTarget.value) || undefined + ) + } + /> + props.setter("city", evt.currentTarget.value)} + /> + + + ); + }; + + const createCustomerAddressSetter = (alternative = false) => { + const addressField = alternative ? "alternativeAddress" : "debtorAddress"; + + return (name: any, value: any) => { + setState("customer", addressField, name, value); + }; + }; + + const contactSetter = (name: any, value: any) => { + setLocalState("contact", name, value); + }; + + const fullWidthLabelWidth = "50%"; + const FullWidthAccordionInput: Component[0]> = ( + props + ) => ( +
+ +
+ ); + + const saveProject = () => { + const fileContent = JSON.stringify( + { + state: unwrap(state), + localState: unwrap(localState), + }, + null, + " " + ); + + saveFile( + `rappli-${ + state.project.projectNumber.length + ? state.project.projectNumber.replaceAll(" ", "-") + "-" + : "" + }${format(fromUnixTime(state.project.date), "yyyy-MM-dd")}.json`, + "application/json", + fileContent + ); + }; + + const saveOnCtrlS = (e: KeyboardEvent) => { + if (e.ctrlKey && e.key === "s") { + e.preventDefault(); + saveProject(); + } + }; + + onMount(function () { + document.addEventListener("keydown", saveOnCtrlS); + }); + + onCleanup(function () { + document.removeEventListener("keydown", saveOnCtrlS); + }); + + return ( + <> +
+
+ + + Dokument + + + + + } + > + {/* TODO: Add option for item price decimals */} + + + + setState( + "project", + "projectNumber", + evt.currentTarget.value + ) + } + /> + + setState("project", "orderNumber", evt.currentTarget.value) + } + /> +
+ setState("project", "date", v)} + /> +
+ + setState( + "project", + "deliveryNumber", + evt.currentTarget.value + ) + } + /> + +
+ setState("project", "deliveryDate", v)} + /> +
+
+
+ + Typ neuer Positionen + + +
+
+ + + setState( + "defaultItemPrice", + parseFloat(evt.currentTarget.value) || 0 + ) + } + /> + +
+