Changes
This commit is contained in:
parent
0d30b03b93
commit
90924073ed
6 changed files with 102 additions and 74 deletions
|
@ -1,6 +1,5 @@
|
||||||
import { Atom, useAtom } from "jotai";
|
import { Atom, useAtom } from "jotai";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { Form } from "react-bootstrap";
|
|
||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
|
|
|
@ -12,6 +12,25 @@ export default function Layout({ children }) {
|
||||||
<Container>
|
<Container>
|
||||||
<main>{children}</main>
|
<main>{children}</main>
|
||||||
</Container>
|
</Container>
|
||||||
|
|
||||||
|
<footer style={{ textAlign: "center" }}>
|
||||||
|
<small>
|
||||||
|
<a href="https://github.com/iptq/wisesplit/" target="_blank">
|
||||||
|
[source]
|
||||||
|
</a>
|
||||||
|
·
|
||||||
|
<a href="https://www.gnu.org/licenses/agpl-3.0.txt" target="_blank">
|
||||||
|
[license]
|
||||||
|
</a>
|
||||||
|
·
|
||||||
|
<a href="https://github.com/iptq/wisesplit/stargazers">
|
||||||
|
<img
|
||||||
|
alt="GitHub stars"
|
||||||
|
src="https://img.shields.io/github/stars/iptq/wisesplit?style=social"
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
</small>
|
||||||
|
</footer>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { Atom, useAtom } from "jotai";
|
import { Atom, useAtom, WritableAtom } from "jotai";
|
||||||
import { Badge, ListGroup } from "react-bootstrap";
|
import { Badge, ListGroup } from "react-bootstrap";
|
||||||
|
|
||||||
export interface IPerson {
|
export interface IPerson {
|
||||||
|
@ -7,6 +7,7 @@ export interface IPerson {
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
personAtom: Atom<IPerson>;
|
personAtom: Atom<IPerson>;
|
||||||
|
splitBetweenAtom: WritableAtom<Atom<IPerson>[]>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function Person({ personAtom, splitBetweenAtom }: Props) {
|
export default function Person({ personAtom, splitBetweenAtom }: Props) {
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import { atom, Atom, useAtom, useAtomValue } from "jotai";
|
import { Atom, useAtom } from "jotai";
|
||||||
import { useState } from "react";
|
import { Badge, Card } from "react-bootstrap";
|
||||||
import { Badge, Card, ListGroup } from "react-bootstrap";
|
|
||||||
import { receiptAtom } from "../lib/state";
|
import { receiptAtom } from "../lib/state";
|
||||||
import EditBox from "./EditBox";
|
import EditBox from "./EditBox";
|
||||||
import Person, { IPerson } from "./Person";
|
import { IPerson } from "./Person";
|
||||||
|
import SplitBetween from "./SplitBetween";
|
||||||
|
|
||||||
export interface IReceiptItem {
|
export interface IReceiptItem {
|
||||||
name: string;
|
name: string;
|
||||||
|
@ -11,61 +11,14 @@ export interface IReceiptItem {
|
||||||
splitBetween: Atom<Atom<IPerson>[]>;
|
splitBetween: Atom<Atom<IPerson>[]>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Props {
|
|
||||||
itemAtom: Atom<IReceiptItem>;
|
|
||||||
}
|
|
||||||
|
|
||||||
function SplitBetween({ splitBetweenAtom }) {
|
|
||||||
const [splitBetween, setSplitBetween] = useAtom(splitBetweenAtom);
|
|
||||||
const [input, setInput] = useState("");
|
|
||||||
const [editing, setEditing] = useState(false);
|
|
||||||
|
|
||||||
const startEditing = (_) => {
|
|
||||||
setInput("");
|
|
||||||
setEditing(true);
|
|
||||||
};
|
|
||||||
|
|
||||||
const addPerson = (e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
setSplitBetween([...splitBetween, atom({ name: input })]);
|
|
||||||
setEditing(false);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
Split between ({splitBetween.length}):
|
|
||||||
<ListGroup horizontal>
|
|
||||||
{splitBetween.map((a, i) => (
|
|
||||||
<Person
|
|
||||||
personAtom={a}
|
|
||||||
key={`split-${i}`}
|
|
||||||
splitBetweenAtom={splitBetweenAtom}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
<ListGroup.Item onClick={startEditing}>
|
|
||||||
{editing ? (
|
|
||||||
<form onSubmit={addPerson}>
|
|
||||||
<input
|
|
||||||
autoFocus={true}
|
|
||||||
type="text"
|
|
||||||
value={input}
|
|
||||||
onBlur={(_) => setEditing(false)}
|
|
||||||
onInput={(e) => setInput(e.target.value)}
|
|
||||||
/>
|
|
||||||
</form>
|
|
||||||
) : (
|
|
||||||
"[+]"
|
|
||||||
)}
|
|
||||||
</ListGroup.Item>
|
|
||||||
</ListGroup>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function Price({ priceAtom }) {
|
function Price({ priceAtom }) {
|
||||||
return <EditBox valueAtom={priceAtom} />;
|
return <EditBox valueAtom={priceAtom} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface Props {
|
||||||
|
itemAtom: Atom<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);
|
||||||
|
@ -76,17 +29,24 @@ export default function ReceiptItem({ itemAtom }: Props) {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card>
|
<Card>
|
||||||
|
<Card.Header>
|
||||||
|
<Card.Title className="d-flex justify-content-between align-items-center">
|
||||||
|
<h3>{item.name}</h3>
|
||||||
|
<span>
|
||||||
|
<Price priceAtom={item.price} />
|
||||||
|
<Badge
|
||||||
|
bg="danger"
|
||||||
|
pill
|
||||||
|
onClick={removeSelf}
|
||||||
|
style={{ cursor: "pointer" }}
|
||||||
|
>
|
||||||
|
×
|
||||||
|
</Badge>
|
||||||
|
</span>
|
||||||
|
</Card.Title>
|
||||||
|
</Card.Header>
|
||||||
|
|
||||||
<Card.Body>
|
<Card.Body>
|
||||||
<Card.Title>{item.name}</Card.Title>
|
|
||||||
Item price: <Price priceAtom={item.price} />
|
|
||||||
<Badge
|
|
||||||
bg="danger"
|
|
||||||
pill
|
|
||||||
onClick={removeSelf}
|
|
||||||
style={{ cursor: "pointer" }}
|
|
||||||
>
|
|
||||||
×
|
|
||||||
</Badge>
|
|
||||||
<SplitBetween splitBetweenAtom={item.splitBetween} />
|
<SplitBetween splitBetweenAtom={item.splitBetween} />
|
||||||
</Card.Body>
|
</Card.Body>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
55
components/SplitBetween.tsx
Normal file
55
components/SplitBetween.tsx
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
import { atom, Atom, useAtom } from "jotai";
|
||||||
|
import { useState } from "react";
|
||||||
|
import { ListGroup } from "react-bootstrap";
|
||||||
|
import Person, { IPerson } from "./Person";
|
||||||
|
|
||||||
|
export interface Props {
|
||||||
|
splitBetweenAtom: Atom<Atom<IPerson>[]>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function SplitBetween({ splitBetweenAtom }: Props) {
|
||||||
|
const [splitBetween, setSplitBetween] = useAtom(splitBetweenAtom);
|
||||||
|
const [input, setInput] = useState("");
|
||||||
|
const [editing, setEditing] = useState(false);
|
||||||
|
|
||||||
|
const startEditing = (_) => {
|
||||||
|
setInput("");
|
||||||
|
setEditing(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const addPerson = (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
setSplitBetween([...splitBetween, atom({ name: input })]);
|
||||||
|
setEditing(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
Split between ({splitBetween.length}):
|
||||||
|
<ListGroup horizontal>
|
||||||
|
{splitBetween.map((a, i) => (
|
||||||
|
<Person
|
||||||
|
personAtom={a}
|
||||||
|
key={`split-${i}`}
|
||||||
|
splitBetweenAtom={splitBetweenAtom}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
<ListGroup.Item onClick={startEditing}>
|
||||||
|
{editing ? (
|
||||||
|
<form onSubmit={addPerson}>
|
||||||
|
<input
|
||||||
|
autoFocus={true}
|
||||||
|
type="text"
|
||||||
|
value={input}
|
||||||
|
onBlur={(_) => setEditing(false)}
|
||||||
|
onInput={(e) => setInput(e.target.value)}
|
||||||
|
/>
|
||||||
|
</form>
|
||||||
|
) : (
|
||||||
|
"[+]"
|
||||||
|
)}
|
||||||
|
</ListGroup.Item>
|
||||||
|
</ListGroup>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
|
@ -42,7 +42,7 @@ const Home: NextPage = () => {
|
||||||
placeholder="Add item..."
|
placeholder="Add item..."
|
||||||
onInput={(e) => setInput(e.target.value)}
|
onInput={(e) => setInput(e.target.value)}
|
||||||
value={input}
|
value={input}
|
||||||
style={{ padding: "8px", fontSize: "1.5em" }}
|
style={{ padding: "8px 16px", fontSize: "1.5em" }}
|
||||||
/>
|
/>
|
||||||
</Form>
|
</Form>
|
||||||
|
|
||||||
|
@ -70,12 +70,6 @@ const Home: NextPage = () => {
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<small>
|
|
||||||
<a href="https://github.com/iptq/wisesplit/">[source]</a>
|
|
||||||
·
|
|
||||||
<a href="https://www.gnu.org/licenses/agpl-3.0.txt">[license]</a>
|
|
||||||
</small>
|
|
||||||
</main>
|
</main>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in a new issue