Compare commits

...

3 Commits

@ -2,6 +2,14 @@
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
## [1.7.0](https://git.lufrai.com/rappli/rappli/compare/v1.6.0...v1.7.0) (2022-10-18)
### Features
* implement apply bank address buttons ([f501579](https://git.lufrai.com/rappli/rappli/commit/f50157964473a9bf9dba85f456eda03c516a8210))
* implement second name field for postal addresses ([e0f5a05](https://git.lufrai.com/rappli/rappli/commit/e0f5a05c3adec5fa992426d3445cc4a8d995bd02)), closes [#25](https://git.lufrai.com/rappli/rappli/issues/25)
## [1.6.0](https://git.lufrai.com/rappli/rappli/compare/v1.5.1...v1.6.0) (2022-09-26) ## [1.6.0](https://git.lufrai.com/rappli/rappli/compare/v1.5.1...v1.6.0) (2022-09-26)

4
package-lock.json generated

@ -1,12 +1,12 @@
{ {
"name": "rappli", "name": "rappli",
"version": "1.6.0", "version": "1.7.0",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "rappli", "name": "rappli",
"version": "1.6.0", "version": "1.7.0",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"solid-start": "v0.1.0-alpha.89", "solid-start": "v0.1.0-alpha.89",

@ -1,6 +1,6 @@
{ {
"name": "rappli", "name": "rappli",
"version": "1.6.0", "version": "1.7.0",
"bin": "./bin/rappli.js", "bin": "./bin/rappli.js",
"scripts": { "scripts": {
"dev": "solid-start dev", "dev": "solid-start dev",

@ -1,7 +1,7 @@
import { Component } from "solid-js"; import { Component, Show } from "solid-js";
import z, { Infer } from "myzod"; import z, { Infer } from "myzod";
export const addressSchema = z.object({ const addressSchema_ = () => ({
type: z.literals("S", "K").optional(), type: z.literals("S", "K").optional(),
name: z.string(), name: z.string(),
line1: z.string().optional(), line1: z.string().optional(),
@ -11,9 +11,20 @@ export const addressSchema = z.object({
country: z.string(), country: z.string(),
}); });
export const addressSchema = z.object(addressSchema_());
export const postalAddressSchema = z.object({
...addressSchema_(),
name2: z.string().optional(),
});
export type AddressData = Infer<typeof addressSchema>; export type AddressData = Infer<typeof addressSchema>;
export const createAddress = (data: Partial<AddressData> = {}): AddressData => { export type PostalAddressData = Infer<typeof postalAddressSchema>;
export const createAddress = <T extends AddressData = AddressData>(
data: Partial<T> = {}
): T => {
return { return {
name: "", name: "",
line1: "", line1: "",
@ -22,7 +33,7 @@ export const createAddress = (data: Partial<AddressData> = {}): AddressData => {
zip: undefined, zip: undefined,
country: "CH", country: "CH",
...data, ...data,
}; } as any;
}; };
export const isStructuredAddress = (address: AddressData) => { export const isStructuredAddress = (address: AddressData) => {
@ -49,12 +60,23 @@ export const getLine2 = (address: AddressData) => {
return address.line2; return address.line2;
}; };
const Address: Component<{ address: AddressData }> = (props) => ( const Address: Component<{ address: PostalAddressData }> = (props) => (
<> <>
<div>{props.address.name}</div> <div>{props.address.name}</div>
<Show when={props.address.name2}>
<div>{props.address.name2}</div>
</Show>
<div>{getLine1(props.address)}</div> <div>{getLine1(props.address)}</div>
<div>{getLine2(props.address)}</div> <div>{getLine2(props.address)}</div>
</> </>
); );
export const applyAddress = (target: AddressData, values: AddressData) => {
target.name = values.name;
target.line1 = values.line1;
target.line2 = values.line2;
target.zip = values.zip;
target.city = values.city;
};
export default Address; export default Address;

@ -10,8 +10,9 @@ import {
createMemo, createMemo,
onMount, onMount,
onCleanup, onCleanup,
mergeProps,
} from "solid-js"; } from "solid-js";
import { createStore, reconcile, unwrap } from "solid-js/store"; import { createStore, produce, reconcile, unwrap } from "solid-js/store";
import { format, fromUnixTime, getUnixTime } from "date-fns"; import { format, fromUnixTime, getUnixTime } from "date-fns";
import z from "myzod"; import z from "myzod";
import Big from "big.js"; import Big from "big.js";
@ -51,7 +52,11 @@ import {
storeSchema, storeSchema,
UiStoreContext, UiStoreContext,
} from "~/stores"; } from "~/stores";
import { AddressData, isStructuredAddress } from "../Address"; import {
applyAddress,
isStructuredAddress,
PostalAddressData,
} from "../Address";
import PositionsIcon from "~icons/carbon/show-data-cards"; import PositionsIcon from "~icons/carbon/show-data-cards";
import YouIcon from "~icons/carbon/face-wink"; import YouIcon from "~icons/carbon/face-wink";
import DesignIcon from "~icons/carbon/paint-brush"; import DesignIcon from "~icons/carbon/paint-brush";
@ -110,11 +115,13 @@ const SettingsOverlay: Component = () => {
const [CustomerValidationContext, customerDataForm] = createValidation(); const [CustomerValidationContext, customerDataForm] = createValidation();
const AddressInputs: Component<{ const AddressInputs: Component<{
postal?: boolean;
namePrefix?: string; namePrefix?: string;
nameRequired?: boolean; nameRequired?: boolean;
setter: (name: string, value: any) => void; setter: (name: string, value: any) => void;
address: () => AddressData; address: () => PostalAddressData;
}> = (props) => { }> = (p) => {
const props = mergeProps({ postal: false }, p);
const isStructured = createMemo(() => isStructuredAddress(props.address())); const isStructured = createMemo(() => isStructuredAddress(props.address()));
const withPrefix = (name: string) => const withPrefix = (name: string) =>
createMemo( createMemo(
@ -123,7 +130,11 @@ const SettingsOverlay: Component = () => {
return ( return (
<> <>
<div class="col-span-2"> <div
classList={{
"col-span-2": !props.postal,
}}
>
<TextInput <TextInput
name={withPrefix("name")} name={withPrefix("name")}
label="Name" label="Name"
@ -134,6 +145,15 @@ const SettingsOverlay: Component = () => {
/> />
</div> </div>
<Show when={props.nameRequired || props.address().name}> <Show when={props.nameRequired || props.address().name}>
<Show when={props.postal}>
<TextInput
name={withPrefix("name2")}
label="Name 2"
maxLength={70}
value={props.address().name2}
onInput={(evt) => props.setter("name2", evt.currentTarget.value)}
/>
</Show>
<TextInput <TextInput
name={withPrefix("line1")} name={withPrefix("line1")}
label={isStructured() ? "Strasse" : "Linie 1"} label={isStructured() ? "Strasse" : "Linie 1"}
@ -581,11 +601,29 @@ const SettingsOverlay: Component = () => {
<AddressInputs <AddressInputs
namePrefix="customAddress" namePrefix="customAddress"
nameRequired={true} nameRequired={true}
postal
setter={(name, value) => { setter={(name, value) => {
setLocalState("customAddress", name as any, value); setLocalState("customAddress", name as any, value);
}} }}
address={() => localState.customAddress} address={() => localState.customAddress}
/> />
<div class="col-span-2">
<button
class="btn btn-xs btn-accent btn-block"
onClick={() => {
setLocalState(
produce((s) => {
applyAddress(
s.customAddress,
localState.creditor
);
})
);
}}
>
Von Bank Verbindung übernehmen
</button>
</div>
</AccordionItemGrid> </AccordionItemGrid>
</Show> </Show>
</div> </div>
@ -705,9 +743,28 @@ const SettingsOverlay: Component = () => {
<Show when={state.useCustomerAlternativeAddress}> <Show when={state.useCustomerAlternativeAddress}>
<AccordionItemGrid> <AccordionItemGrid>
<AddressInputs <AddressInputs
nameRequired
postal
setter={createCustomerAddressSetter(true)} setter={createCustomerAddressSetter(true)}
address={() => state.customer.alternativeAddress} address={() => state.customer.alternativeAddress}
/> />
<div class="col-span-2">
<button
class="btn btn-xs btn-accent btn-block"
onClick={() => {
setState(
produce((s) => {
applyAddress(
s.customer.alternativeAddress,
state.customer.debtorAddress
);
})
);
}}
>
Von Bank Verbindung übernehmen
</button>
</div>
</AccordionItemGrid> </AccordionItemGrid>
</Show> </Show>
</div> </div>

@ -16,7 +16,12 @@ import Page from "~/components/Page";
import LufraiLogoWww from "~icons/custom/lufrai-logo-www"; import LufraiLogoWww from "~icons/custom/lufrai-logo-www";
import AppIcon from "~icons/custom/icon"; import AppIcon from "~icons/custom/icon";
import { getLine1, getLine2, isStructuredAddress } from "~/components/Address"; import Address, {
getLine1,
getLine2,
isStructuredAddress,
PostalAddressData,
} from "~/components/Address";
import Positions, { import Positions, {
calculatePositionsPrice, calculatePositionsPrice,
calculatePositionsTax, calculatePositionsTax,
@ -124,7 +129,7 @@ export default function Home() {
); );
}; };
const address = createMemo(() => { const address = createMemo<PostalAddressData>(() => {
const value = const value =
(localState.useCustomAddress && localState.customAddress (localState.useCustomAddress && localState.customAddress
? localState.customAddress ? localState.customAddress
@ -218,15 +223,18 @@ export default function Home() {
> >
<div class="text-sm mb-3"> <div class="text-sm mb-3">
{address().name {address().name
? [address().name, getLine1(address()), getLine2(address())] ? [
.filter((x) => x != "") address().name,
.join(" · ") address().name2,
getLine1(address()),
getLine2(address()),
]
.filter((x) => x != "" && x != null)
.join(" ∙ ")
: ""} : ""}
</div> </div>
<div class="text-lg leading-snug"> <div class="text-lg leading-snug">
<div>{customerAddress().name}</div> <Address address={customerAddress()} />
<div>{getLine1(customerAddress())}</div>
<div>{getLine2(customerAddress())}</div>
</div> </div>
</div> </div>
<div> <div>

@ -1,6 +1,11 @@
import { createContext } from "solid-js"; import { createContext } from "solid-js";
import { createStore as createStore_ } from "solid-js/store"; import { createStore as createStore_ } from "solid-js/store";
import { addressSchema, createAddress } from "./components/Address"; import {
addressSchema,
createAddress,
PostalAddressData,
postalAddressSchema,
} from "./components/Address";
import { createLocalStore as createLocalStore_ } from "./localStore"; import { createLocalStore as createLocalStore_ } from "./localStore";
import { getUnixTime } from "date-fns"; import { getUnixTime } from "date-fns";
import z, { Infer } from "myzod"; import z, { Infer } from "myzod";
@ -84,7 +89,7 @@ export const storeSchema = z.object({
customer: z.object({ customer: z.object({
customerNumber: z.string(), customerNumber: z.string(),
vatNumber: z.string(), vatNumber: z.string(),
alternativeAddress: addressSchema, alternativeAddress: postalAddressSchema,
debtorAddress: addressSchema, debtorAddress: addressSchema,
}), }),
defaultItemPrice: z.number(), defaultItemPrice: z.number(),
@ -119,7 +124,9 @@ export const createStore = () =>
customer: { customer: {
customerNumber: "", customerNumber: "",
vatNumber: "", vatNumber: "",
alternativeAddress: createAddress(), alternativeAddress: createAddress<PostalAddressData>({
name2: undefined,
}),
debtorAddress: createAddress(), debtorAddress: createAddress(),
}, },
defaultPositionType: POSITION_TYPE_QUANTITY, defaultPositionType: POSITION_TYPE_QUANTITY,
@ -143,7 +150,7 @@ export const localStoreSchema = z.object({
creditor: addressSchema, creditor: addressSchema,
addressLayout: z.literals(ADDRESS_LAYOUT_LEFT, ADDRESS_LAYOUT_RIGHT), addressLayout: z.literals(ADDRESS_LAYOUT_LEFT, ADDRESS_LAYOUT_RIGHT),
font: z.string(), font: z.string(),
customAddress: addressSchema, customAddress: postalAddressSchema,
contact: z.object({ contact: z.object({
name: z.string(), name: z.string(),
phone: z.string(), phone: z.string(),
@ -195,7 +202,7 @@ export const createLocalStore = () =>
creditor: createAddress(), creditor: createAddress(),
font: "default", font: "default",
addressLayout: ADDRESS_LAYOUT_RIGHT, addressLayout: ADDRESS_LAYOUT_RIGHT,
customAddress: createAddress(), customAddress: createAddress<PostalAddressData>({ name2: undefined }),
contact: { contact: {
name: "", name: "",
phone: "", phone: "",

Loading…
Cancel
Save