Fix typescript errors
This commit is contained in:
parent
c9750fdc5f
commit
4be8903816
11 changed files with 77 additions and 72 deletions
|
@ -1,13 +1,9 @@
|
||||||
import { Atom, useAtom } from "jotai";
|
import { PrimitiveAtom, useAtom } from "jotai";
|
||||||
import { useState } from "react";
|
import { SyntheticEvent, useState } from "react";
|
||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
|
|
||||||
export interface CanBeConvertedToString {
|
export interface Props<T> {
|
||||||
toString(): string;
|
valueAtom: PrimitiveAtom<T>;
|
||||||
}
|
|
||||||
|
|
||||||
export interface Props<T extends CanBeConvertedToString> {
|
|
||||||
valueAtom: Atom<T>;
|
|
||||||
formatter?: (arg: T) => string;
|
formatter?: (arg: T) => string;
|
||||||
inputType?: string;
|
inputType?: string;
|
||||||
validator: (arg: string) => T | null;
|
validator: (arg: string) => T | null;
|
||||||
|
@ -33,7 +29,7 @@ const EditingBox = styled.input`
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export default function EditBox<T extends CanBeConvertedToString>({
|
export default function EditBox<T>({
|
||||||
valueAtom,
|
valueAtom,
|
||||||
formatter,
|
formatter,
|
||||||
inputType,
|
inputType,
|
||||||
|
@ -44,11 +40,11 @@ export default function EditBox<T extends CanBeConvertedToString>({
|
||||||
const [editing, setEditing] = useState(false);
|
const [editing, setEditing] = useState(false);
|
||||||
|
|
||||||
const startEditing = (_: any) => {
|
const startEditing = (_: any) => {
|
||||||
setValueInput(value.toString());
|
setValueInput(String(value));
|
||||||
setEditing(true);
|
setEditing(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
const finalize = (e: Event) => {
|
const finalize = (e: SyntheticEvent) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const validateResult = validator(valueInput);
|
const validateResult = validator(valueInput);
|
||||||
if (validateResult !== null) {
|
if (validateResult !== null) {
|
||||||
|
@ -66,14 +62,14 @@ export default function EditBox<T extends CanBeConvertedToString>({
|
||||||
step="0.01"
|
step="0.01"
|
||||||
value={valueInput}
|
value={valueInput}
|
||||||
onBlur={finalize}
|
onBlur={finalize}
|
||||||
onInput={(e) => setValueInput(e.target.value)}
|
onInput={(e) => setValueInput(e.currentTarget.value)}
|
||||||
/>
|
/>
|
||||||
</form>
|
</form>
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return (
|
return (
|
||||||
<ClickableContainer onClick={startEditing}>
|
<ClickableContainer onClick={startEditing}>
|
||||||
{formatter ? formatter(value) : value.toString()}
|
{formatter ? formatter(value) : String(value)}
|
||||||
</ClickableContainer>
|
</ClickableContainer>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
import { Container, Navbar } from "react-bootstrap";
|
import { Container, Navbar } from "react-bootstrap";
|
||||||
|
|
||||||
export default function Layout({ children }) {
|
export interface Props {
|
||||||
|
children: JSX.Element;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function Layout({ children }: Props) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Navbar bg="dark" variant="dark" expand="lg">
|
<Navbar bg="dark" variant="dark" expand="lg">
|
||||||
|
@ -31,6 +35,8 @@ export default function Layout({ children }) {
|
||||||
[license]
|
[license]
|
||||||
</a>
|
</a>
|
||||||
·
|
·
|
||||||
|
|
||||||
|
{/* eslint-disable @next/next/no-img-element */}
|
||||||
<a href="https://github.com/iptq/wisesplit/stargazers">
|
<a href="https://github.com/iptq/wisesplit/stargazers">
|
||||||
<img
|
<img
|
||||||
alt="GitHub stars"
|
alt="GitHub stars"
|
||||||
|
|
|
@ -1,16 +1,16 @@
|
||||||
import { Atom } from "jotai";
|
import { PrimitiveAtom } from "jotai";
|
||||||
import EditBox from "./EditBox";
|
import EditBox from "./EditBox";
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
valueAtom: Atom<number>;
|
valueAtom: PrimitiveAtom<number>;
|
||||||
formatter?: (arg: number) => string;
|
formatter?: (arg: number) => string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function NumberEditBox({ valueAtom, formatter }: Props) {
|
export default function NumberEditBox({ valueAtom, formatter }: Props) {
|
||||||
const validator = (arg: string) => {
|
const validator = (arg: string): number | null => {
|
||||||
try {
|
try {
|
||||||
const n = parseFloat(arg);
|
const n = parseFloat(arg);
|
||||||
if (isNaN(n) || !isFinite(n)) return;
|
if (isNaN(n) || !isFinite(n)) return null;
|
||||||
return n;
|
return n;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -1,21 +1,21 @@
|
||||||
import { Atom, useAtom, WritableAtom } from "jotai";
|
import { PrimitiveAtom, useAtom } from "jotai";
|
||||||
import { Badge, ListGroup } from "react-bootstrap";
|
import { Badge } from "react-bootstrap";
|
||||||
import EditBox from "./EditBox";
|
import EditBox from "./EditBox";
|
||||||
|
|
||||||
export interface IPerson {
|
export interface IPerson {
|
||||||
name: Atom<string>;
|
name: PrimitiveAtom<string>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
personAtom: Atom<IPerson>;
|
personAtom: PrimitiveAtom<IPerson>;
|
||||||
splitBetweenAtom: Atom<Atom<IPerson>[]>;
|
splitBetweenAtom: PrimitiveAtom<PrimitiveAtom<IPerson>[]>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function Person({ personAtom, splitBetweenAtom }: Props) {
|
export default function Person({ personAtom, splitBetweenAtom }: Props) {
|
||||||
const [person] = useAtom(personAtom);
|
const [person] = useAtom(personAtom);
|
||||||
const [splitBetween, setSplitBetween] = useAtom(splitBetweenAtom);
|
const [splitBetween, setSplitBetween] = useAtom(splitBetweenAtom);
|
||||||
|
|
||||||
const removeSelf = (_) => {
|
const removeSelf = (_: any) => {
|
||||||
setSplitBetween([...splitBetween.filter((x) => x != personAtom)]);
|
setSplitBetween([...splitBetween.filter((x) => x != personAtom)]);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { Atom, useAtom } from "jotai";
|
import { PrimitiveAtom, useAtom } from "jotai";
|
||||||
import { Badge, Card } from "react-bootstrap";
|
import { Badge, Card } from "react-bootstrap";
|
||||||
import { moneyFormatter } from "../lib/formatter";
|
import { moneyFormatter } from "../lib/formatter";
|
||||||
import { receiptAtom } from "../lib/state";
|
import { receiptAtom } from "../lib/state";
|
||||||
|
@ -7,27 +7,23 @@ import NumberEditBox from "./NumberEditBox";
|
||||||
import { IPerson } from "./Person";
|
import { IPerson } from "./Person";
|
||||||
import SplitBetween from "./SplitBetween";
|
import SplitBetween from "./SplitBetween";
|
||||||
|
|
||||||
export interface IReceiptItem {
|
export type Receipt = PrimitiveAtom<IReceiptItem>[];
|
||||||
name: Atom<string>;
|
|
||||||
price: Atom<number>;
|
|
||||||
splitBetween: Atom<Atom<IPerson>[]>;
|
|
||||||
}
|
|
||||||
|
|
||||||
function Price({ priceAtom }) {
|
export interface IReceiptItem {
|
||||||
return (
|
name: PrimitiveAtom<string>;
|
||||||
<NumberEditBox valueAtom={priceAtom} formatter={moneyFormatter.format} />
|
price: PrimitiveAtom<number>;
|
||||||
);
|
splitBetween: PrimitiveAtom<PrimitiveAtom<IPerson>[]>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
itemAtom: Atom<IReceiptItem>;
|
itemAtom: PrimitiveAtom<IReceiptItem>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function ReceiptItem({ itemAtom }: Props) {
|
export default function ReceiptItem({ itemAtom }: Props) {
|
||||||
const [receipt, setReceipt] = useAtom(receiptAtom);
|
const [receipt, setReceipt] = useAtom(receiptAtom);
|
||||||
const [item, _] = useAtom(itemAtom);
|
const [item, _] = useAtom(itemAtom);
|
||||||
|
|
||||||
const removeSelf = (_) => {
|
const removeSelf = (_: any) => {
|
||||||
setReceipt([...receipt.filter((x) => x != itemAtom)]);
|
setReceipt([...receipt.filter((x) => x != itemAtom)]);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -39,7 +35,10 @@ export default function ReceiptItem({ itemAtom }: Props) {
|
||||||
<EditBox valueAtom={item.name} validator={(s) => s} />
|
<EditBox valueAtom={item.name} validator={(s) => s} />
|
||||||
</h3>
|
</h3>
|
||||||
<span>
|
<span>
|
||||||
<Price priceAtom={item.price} />
|
<NumberEditBox
|
||||||
|
valueAtom={item.price}
|
||||||
|
formatter={moneyFormatter.format}
|
||||||
|
/>
|
||||||
<Badge
|
<Badge
|
||||||
bg="danger"
|
bg="danger"
|
||||||
pill
|
pill
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
import { atom, Atom, useAtom } from "jotai";
|
import { atom, PrimitiveAtom, useAtom } from "jotai";
|
||||||
import { useState } from "react";
|
import { SyntheticEvent, useState } from "react";
|
||||||
import { Button, Form, ListGroup } from "react-bootstrap";
|
import { Button, Form } from "react-bootstrap";
|
||||||
import Person, { IPerson } from "./Person";
|
import Person, { IPerson } from "./Person";
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
splitBetweenAtom: Atom<Atom<IPerson>[]>;
|
splitBetweenAtom: PrimitiveAtom<PrimitiveAtom<IPerson>[]>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function SplitBetween({ splitBetweenAtom }: Props) {
|
export default function SplitBetween({ splitBetweenAtom }: Props) {
|
||||||
|
@ -12,14 +12,14 @@ export default function SplitBetween({ splitBetweenAtom }: Props) {
|
||||||
const [input, setInput] = useState("");
|
const [input, setInput] = useState("");
|
||||||
const [editing, setEditing] = useState(false);
|
const [editing, setEditing] = useState(false);
|
||||||
|
|
||||||
const startEditing = (_) => {
|
const startEditing = (_: any) => {
|
||||||
setInput("");
|
setInput("");
|
||||||
setEditing(true);
|
setEditing(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
const addPerson = (e) => {
|
const addPerson = (e: SyntheticEvent) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const person = { name: atom(input) };
|
const person: IPerson = { name: atom(input) };
|
||||||
setSplitBetween([...splitBetween, atom(person)]);
|
setSplitBetween([...splitBetween, atom(person)]);
|
||||||
setEditing(false);
|
setEditing(false);
|
||||||
};
|
};
|
||||||
|
@ -43,7 +43,7 @@ export default function SplitBetween({ splitBetweenAtom }: Props) {
|
||||||
value={input}
|
value={input}
|
||||||
placeholder="Add person to split with..."
|
placeholder="Add person to split with..."
|
||||||
onBlur={(_) => setEditing(false)}
|
onBlur={(_) => setEditing(false)}
|
||||||
onInput={(e) => setInput(e.target.value)}
|
onInput={(e) => setInput(e.currentTarget.value)}
|
||||||
/>
|
/>
|
||||||
</Form>
|
</Form>
|
||||||
) : (
|
) : (
|
||||||
|
|
|
@ -4,7 +4,11 @@ export interface ParsedInput {
|
||||||
splitBetween: Set<string>;
|
splitBetween: Set<string>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ParsedInputDisplay({ input }) {
|
export interface Props {
|
||||||
|
input: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ParsedInputDisplay({ input }: Props) {
|
||||||
const parsed = parseInput(input);
|
const parsed = parseInput(input);
|
||||||
|
|
||||||
const formatter = new Intl.NumberFormat("en-US", {
|
const formatter = new Intl.NumberFormat("en-US", {
|
||||||
|
|
33
lib/state.ts
33
lib/state.ts
|
@ -1,5 +1,7 @@
|
||||||
import { atom, PrimitiveAtom } from "jotai";
|
import { atom, PrimitiveAtom } from "jotai";
|
||||||
import { IReceiptItem } from "../components/ReceiptItem";
|
import { SetAtom } from "jotai/core/atom";
|
||||||
|
import { IPerson } from "../components/Person";
|
||||||
|
import { IReceiptItem, Receipt } from "../components/ReceiptItem";
|
||||||
import parseInput from "./parseInput";
|
import parseInput from "./parseInput";
|
||||||
|
|
||||||
export const totalAtom = atom(0);
|
export const totalAtom = atom(0);
|
||||||
|
@ -39,16 +41,23 @@ export const receiptTotalAtom = atom((get) => {
|
||||||
return { subtotal: subtotalSum, totalMap: newTotals };
|
return { subtotal: subtotalSum, totalMap: newTotals };
|
||||||
});
|
});
|
||||||
|
|
||||||
export function addLine(line: string, setReceipt) {
|
export function addLine(
|
||||||
|
line: string,
|
||||||
|
receipt: Receipt,
|
||||||
|
setReceipt: SetAtom<Receipt, void>
|
||||||
|
) {
|
||||||
let parsed = parseInput(line);
|
let parsed = parseInput(line);
|
||||||
console.log(parsed);
|
|
||||||
const name = atom(parsed.itemName);
|
const name: PrimitiveAtom<string> = atom(parsed.itemName);
|
||||||
const price = atom(parsed.price || 0);
|
const price: PrimitiveAtom<number> = atom(parsed.price || 0);
|
||||||
const splitBetween = atom(
|
const splitBetween: PrimitiveAtom<PrimitiveAtom<IPerson>[]> = atom<
|
||||||
[...parsed.splitBetween].map((a) => atom({ name: a }))
|
PrimitiveAtom<IPerson>[]
|
||||||
);
|
>([...parsed.splitBetween].map((a) => atom<IPerson>({ name: atom(a) })));
|
||||||
setReceipt((prev) => [
|
|
||||||
...prev,
|
const newReceiptItem = atom({
|
||||||
atom<IReceiptItem>({ name, price, splitBetween }),
|
name,
|
||||||
]);
|
price,
|
||||||
|
splitBetween,
|
||||||
|
});
|
||||||
|
setReceipt([...receipt, newReceiptItem]);
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,10 +7,7 @@ const nextConfig = {
|
||||||
styledComponents: true,
|
styledComponents: true,
|
||||||
},
|
},
|
||||||
|
|
||||||
typescript: {
|
typescript: {},
|
||||||
// TODO: Move fast and break things lmao
|
|
||||||
ignoreBuildErrors: true,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = nextConfig;
|
module.exports = nextConfig;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { useAtom } from "jotai";
|
import { useAtom } from "jotai";
|
||||||
import type { NextPage } from "next";
|
import type { NextPage } from "next";
|
||||||
import { useState } from "react";
|
import { SyntheticEvent, useState } from "react";
|
||||||
import { Form } from "react-bootstrap";
|
import { Form } from "react-bootstrap";
|
||||||
import NumberEditBox from "../components/NumberEditBox";
|
import NumberEditBox from "../components/NumberEditBox";
|
||||||
import ReceiptItem from "../components/ReceiptItem";
|
import ReceiptItem from "../components/ReceiptItem";
|
||||||
|
@ -24,9 +24,9 @@ const Home: NextPage = () => {
|
||||||
currency: "USD",
|
currency: "USD",
|
||||||
});
|
});
|
||||||
|
|
||||||
const add = (e) => {
|
const add = (e: SyntheticEvent) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
addLine(input, setReceipt);
|
addLine(input, receipt, setReceipt);
|
||||||
setInput("");
|
setInput("");
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
@ -42,7 +42,7 @@ const Home: NextPage = () => {
|
||||||
autoFocus={true}
|
autoFocus={true}
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="Add item..."
|
placeholder="Add item..."
|
||||||
onInput={(e) => setInput(e.target.value)}
|
onInput={(e) => setInput(e.currentTarget.value)}
|
||||||
value={input}
|
value={input}
|
||||||
style={{ padding: "8px 16px", fontSize: "1.5em" }}
|
style={{ padding: "8px 16px", fontSize: "1.5em" }}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -1,6 +0,0 @@
|
||||||
import solid from "solid-start/vite";
|
|
||||||
import { defineConfig } from "vite";
|
|
||||||
|
|
||||||
export default defineConfig({
|
|
||||||
plugins: [solid()],
|
|
||||||
});
|
|
Loading…
Reference in a new issue