Move creation and joining to their own views and components
This commit is contained in:
parent
293e1ca8cd
commit
51c24a2d39
7 changed files with 252 additions and 174 deletions
|
@ -1,138 +0,0 @@
|
|||
import { LitElement, html } from 'lit-element'
|
||||
import './budget-qr-reader'
|
||||
import { QRReadEvent } from './budget-qr-reader'
|
||||
import './input'
|
||||
import './button'
|
||||
import { InputField } from './input'
|
||||
import './icon'
|
||||
|
||||
export class JoinBudgetEvent extends CustomEvent<{ uuid: string }> { }
|
||||
|
||||
export class CreateBudgetEvent extends CustomEvent<{
|
||||
amount: number,
|
||||
currency: string,
|
||||
name: string
|
||||
}> { }
|
||||
|
||||
class BudgetAddComponent extends LitElement {
|
||||
hasCreateError: boolean
|
||||
hasJoinError: boolean
|
||||
showQRReader: boolean
|
||||
|
||||
static get properties() {
|
||||
return {
|
||||
hasCreateError: { type: Boolean },
|
||||
hasJoinError: { type: Boolean },
|
||||
showQRReader: { type: Boolean }
|
||||
}
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
|
||||
this.hasCreateError = false
|
||||
this.hasJoinError = false
|
||||
this.showQRReader = false
|
||||
}
|
||||
|
||||
onCreate(e: Event) {
|
||||
e.preventDefault()
|
||||
const root = this.shadowRoot
|
||||
const amountInputEl = <InputField>root.getElementById('create-budget-amount')
|
||||
const currencyInputEl = <InputField>root.getElementById('create-budget-currency')
|
||||
const nameInputEl = <InputField>root.getElementById('create-budget-name')
|
||||
|
||||
const name = nameInputEl.getValue().trim()
|
||||
const amount = parseInt(amountInputEl.getValue())
|
||||
if (isNaN(amount)) {
|
||||
this.hasCreateError = true
|
||||
return
|
||||
}
|
||||
|
||||
this.dispatchEvent(new CreateBudgetEvent(
|
||||
'createBudget',
|
||||
{ detail: { amount, currency: currencyInputEl.getValue(), name } }
|
||||
))
|
||||
|
||||
amountInputEl.clearValue()
|
||||
currencyInputEl.clearValue()
|
||||
nameInputEl.clearValue()
|
||||
}
|
||||
|
||||
onJoin(e: Event) {
|
||||
e.preventDefault()
|
||||
const inputEl = <InputField>this.shadowRoot.getElementById('join-budget-uuid')
|
||||
|
||||
this.dispatchEvent(new JoinBudgetEvent(
|
||||
'joinBudget',
|
||||
{ detail: { uuid: inputEl.getValue() } }
|
||||
))
|
||||
|
||||
inputEl.clearValue()
|
||||
}
|
||||
|
||||
onQRRead(e: QRReadEvent) {
|
||||
const text = e.detail.text
|
||||
this.dispatchEvent(new JoinBudgetEvent(
|
||||
'joinBudget',
|
||||
{ detail: { uuid: text } }
|
||||
))
|
||||
this.showQRReader = false
|
||||
}
|
||||
|
||||
toggleShowQRReader() {
|
||||
this.showQRReader = !this.showQRReader
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<h2>Create a budget…</h2>
|
||||
<input-field
|
||||
id="create-budget-name"
|
||||
type="text"
|
||||
placeholder="Name (optional)"
|
||||
></input-field>
|
||||
<input-field
|
||||
id="create-budget-amount"
|
||||
type="number"
|
||||
placeholder="Amount"
|
||||
required
|
||||
></input-field>
|
||||
<input-field
|
||||
id="create-budget-currency"
|
||||
type="text"
|
||||
placeholder="Currency (3 chars)"
|
||||
pattern="[A-Z]{3}"
|
||||
maxlength="3"
|
||||
required
|
||||
></input-field>
|
||||
<fa-button @click=${this.onCreate}>Create budget</fa-button>
|
||||
${this.hasCreateError ? html`<p>Could not create budget, check the amount and currency.</p>` : null}
|
||||
|
||||
<h2>… or join existing budget</h2>
|
||||
<p>Add below the budget secret that you can get from the person who created the budget.</p>
|
||||
<input-field
|
||||
id="join-budget-uuid"
|
||||
type="text"
|
||||
placeholder="aaaaaaaa-bbbb-4ccc-9ddd-ffffffffffff"
|
||||
maxlength="36"
|
||||
pattern="[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-4[0-9A-Fa-f]{3}-[89ABab][0-9A-Fa-f]{3}-[0-9A-Fa-f]{12}"
|
||||
required
|
||||
></input-field>
|
||||
<fa-button @click=${this.onJoin}>
|
||||
<fa-icon name="plus" alt="Join budget"></fa-icon>
|
||||
</fa-button>
|
||||
<fa-button type="button" @click=${this.toggleShowQRReader}>
|
||||
<fa-icon name="qrcode" alt="Read QR using camera"></fa-icon>
|
||||
</fa-button>
|
||||
|
||||
<div>
|
||||
${this.showQRReader ? html`<budget-qr-reader @qrRead=${this.onQRRead}></budget-qr-reader>` : null}
|
||||
</div>
|
||||
|
||||
${this.hasJoinError ? html`<p>Could not add given budget, please check it is correctly typed.</p>` : null}
|
||||
`
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define('budget-add', BudgetAddComponent)
|
71
frontend/src/components/budget-create.ts
Normal file
71
frontend/src/components/budget-create.ts
Normal file
|
@ -0,0 +1,71 @@
|
|||
import { LitElement, html } from 'lit-element'
|
||||
import { InputField } from './input'
|
||||
import './input'
|
||||
|
||||
export class CreateBudgetEvent extends CustomEvent<{
|
||||
amount: number,
|
||||
currency: string,
|
||||
name: string
|
||||
}> { }
|
||||
|
||||
class BudgetCreateComponent extends LitElement {
|
||||
hasError: boolean
|
||||
|
||||
static get properties() {
|
||||
return {
|
||||
hasError: { type: Boolean }
|
||||
}
|
||||
}
|
||||
|
||||
onCreate(e: Event) {
|
||||
e.preventDefault()
|
||||
const root = this.shadowRoot
|
||||
const amountInputEl = <InputField>root.getElementById('create-budget-amount')
|
||||
const currencyInputEl = <InputField>root.getElementById('create-budget-currency')
|
||||
const nameInputEl = <InputField>root.getElementById('create-budget-name')
|
||||
|
||||
const name = nameInputEl.getValue().trim()
|
||||
const amount = parseInt(amountInputEl.getValue())
|
||||
if (isNaN(amount)) {
|
||||
this.hasError = true
|
||||
return
|
||||
}
|
||||
|
||||
this.dispatchEvent(new CreateBudgetEvent(
|
||||
'createBudget',
|
||||
{ detail: { amount, currency: currencyInputEl.getValue(), name } }
|
||||
))
|
||||
|
||||
amountInputEl.clearValue()
|
||||
currencyInputEl.clearValue()
|
||||
nameInputEl.clearValue()
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<label for="create-budget-name-field">Name (optional)</label>
|
||||
<input-field
|
||||
id="create-budget-name"
|
||||
type="text"
|
||||
></input-field>
|
||||
<label for="create-budget-amount-field">Amount</label>
|
||||
<input-field
|
||||
id="create-budget-amount"
|
||||
type="number"
|
||||
required
|
||||
></input-field>
|
||||
<label for="create-budget-currency-field">Currency (3 chars)</label>
|
||||
<input-field
|
||||
id="create-budget-currency"
|
||||
type="text"
|
||||
pattern="[A-Z]{3}"
|
||||
maxlength="3"
|
||||
required
|
||||
></input-field>
|
||||
<fa-button @click=${this.onCreate}>Create budget</fa-button>
|
||||
${this.hasError ? html`<p>Could not create budget, check the amount and currency.</p>` : null}
|
||||
`
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define('budget-create', BudgetCreateComponent)
|
|
@ -59,6 +59,8 @@ class BudgetDisplayComponent extends LitElement {
|
|||
display: grid;
|
||||
column-gap: 10px;
|
||||
grid-template: 'now curr' 'of init' / auto 66%;
|
||||
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.money-left {
|
||||
|
@ -191,21 +193,23 @@ class BudgetDisplayComponent extends LitElement {
|
|||
${this.budget.events.map(event => this._renderEvent(this.budget, event))}
|
||||
</ul>
|
||||
|
||||
<fa-button @click=${this.onSpendMoney}>
|
||||
<fa-icon name="credit-card" alt="Spend money"></fa-icon>
|
||||
</fa-button>
|
||||
<fa-button @click=${this.onReset}>
|
||||
<fa-icon name="undo" alt="Reset"></fa-icon>
|
||||
</fa-button>
|
||||
<fa-button @click=${this.onRename}>
|
||||
<fa-icon name="edit" alt="Rename"></fa-icon>
|
||||
</fa-button>
|
||||
<fa-button @click=${this.toggleShare}>
|
||||
<fa-icon name="qrcode" alt="Share"></fa-icon>
|
||||
</fa-button>
|
||||
<fa-button @click=${this.onRemove}>
|
||||
<fa-icon name="trash" alt="Remove"></fa-icon>
|
||||
</fa-button>
|
||||
<div class="actions" aria-label="Actions for budget '${this.budget.name}'">
|
||||
<fa-button @click=${this.onSpendMoney}>
|
||||
<fa-icon name="credit-card" alt="Spend money"></fa-icon>
|
||||
</fa-button>
|
||||
<fa-button @click=${this.onReset}>
|
||||
<fa-icon name="undo" alt="Reset to given amount"></fa-icon>
|
||||
</fa-button>
|
||||
<fa-button @click=${this.onRename}>
|
||||
<fa-icon name="edit" alt="Rename"></fa-icon>
|
||||
</fa-button>
|
||||
<fa-button @click=${this.toggleShare}>
|
||||
<fa-icon name="qrcode" alt="Share with QR code"></fa-icon>
|
||||
</fa-button>
|
||||
<fa-button @click=${this.onRemove}>
|
||||
<fa-icon name="trash" alt="Remove"></fa-icon>
|
||||
</fa-button>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
${this.showShare ? html`<budget-qr .uuid=${this.budget.uuid}></budget-qr>` : null}
|
||||
|
|
70
frontend/src/components/budget-join.ts
Normal file
70
frontend/src/components/budget-join.ts
Normal file
|
@ -0,0 +1,70 @@
|
|||
import { LitElement, html } from 'lit-element'
|
||||
import { InputField } from './input';
|
||||
import { QRReadEvent } from './budget-qr-reader';
|
||||
|
||||
export class JoinBudgetEvent extends CustomEvent<{ uuid: string }> { }
|
||||
|
||||
class BudgetJoinComponent extends LitElement {
|
||||
hasError: boolean
|
||||
showQRReader: boolean
|
||||
|
||||
static get properties() {
|
||||
return {
|
||||
hasError: { type: Boolean },
|
||||
showQRReader: { type: Boolean }
|
||||
}
|
||||
}
|
||||
|
||||
onJoin(e: Event) {
|
||||
e.preventDefault()
|
||||
const inputEl = <InputField>this.shadowRoot.getElementById('join-budget-uuid')
|
||||
|
||||
this.dispatchEvent(new JoinBudgetEvent(
|
||||
'joinBudget',
|
||||
{ detail: { uuid: inputEl.getValue() } }
|
||||
))
|
||||
|
||||
inputEl.clearValue()
|
||||
}
|
||||
|
||||
onQRRead(e: QRReadEvent) {
|
||||
const text = e.detail.text
|
||||
this.dispatchEvent(new JoinBudgetEvent(
|
||||
'joinBudget',
|
||||
{ detail: { uuid: text } }
|
||||
))
|
||||
this.showQRReader = false
|
||||
}
|
||||
|
||||
toggleShowQRReader() {
|
||||
this.showQRReader = !this.showQRReader
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<p>Add below the budget secret that you can get from the person who created the budget.</p>
|
||||
<input-field
|
||||
id="join-budget-uuid"
|
||||
type="text"
|
||||
placeholder="aaaaaaaa-bbbb-4ccc-9ddd-ffffffffffff"
|
||||
maxlength="36"
|
||||
pattern="[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-4[0-9A-Fa-f]{3}-[89ABab][0-9A-Fa-f]{3}-[0-9A-Fa-f]{12}"
|
||||
required
|
||||
></input-field>
|
||||
<fa-button @click=${this.onJoin}>
|
||||
<fa-icon name="plus" alt="Join budget"></fa-icon>
|
||||
</fa-button>
|
||||
<fa-button type="button" @click=${this.toggleShowQRReader}>
|
||||
<fa-icon name="qrcode" alt="Read QR using camera"></fa-icon>
|
||||
</fa-button>
|
||||
|
||||
<div>
|
||||
${this.showQRReader ? html`<budget-qr-reader @qrRead=${this.onQRRead}></budget-qr-reader>` : null}
|
||||
</div>
|
||||
|
||||
${this.hasError ? html`<p>Could not add given budget, please check it is correctly typed.</p>` : null}
|
||||
`
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define('budget-join', BudgetJoinComponent)
|
|
@ -41,7 +41,9 @@ const ICONS = [
|
|||
['qrcode', '\\f029'],
|
||||
['undo', '\\f0e2'],
|
||||
['trash', '\\f1f8'],
|
||||
['plus', '\\f067']
|
||||
['plus', '\\f067'],
|
||||
['arrow-left', '\\f060'],
|
||||
['user-friends', '\\f500']
|
||||
]
|
||||
|
||||
function icon2css(name: string, code: string) {
|
||||
|
|
|
@ -48,7 +48,7 @@ export class InputField extends LitElement {
|
|||
<input
|
||||
id=${this.inputId}
|
||||
type=${this.type}
|
||||
placeholder=${this.placeholder}
|
||||
placeholder=${this.placeholder ? this.placeholder : ''}
|
||||
maxlength=${this.maxlength}
|
||||
pattern=${this.pattern}
|
||||
?required=${this.required}
|
||||
|
|
|
@ -1,14 +1,26 @@
|
|||
import { LitElement, html, css } from 'lit-element'
|
||||
import { LitElement, html, css, TemplateResult } from 'lit-element'
|
||||
import { Budget } from '../services/models/budget'
|
||||
import { CreateBudgetEvent, JoinBudgetEvent } from './budget-add'
|
||||
import './budget-add'
|
||||
import { JoinBudgetEvent } from './budget-join'
|
||||
import './budget-join'
|
||||
import './budget-list'
|
||||
import { readBudget, readEvent } from '../services/readers'
|
||||
import { getBudget, GetError, addEvent, resetBudget, createBudget, CreationError } from '../services/api'
|
||||
import { getFromLocal, storeToLocal } from '../services/storage'
|
||||
import { SpendMoneyEvent, ResetBudgetEvent, RemoveBudgetEvent, RenameBudgetEvent } from './budget-display'
|
||||
import { CreateBudgetEvent } from './budget-create'
|
||||
import './budget-create'
|
||||
import './button'
|
||||
import './icon'
|
||||
|
||||
enum DisplayMode {
|
||||
BudgetView,
|
||||
CreateView,
|
||||
JoinView
|
||||
}
|
||||
|
||||
class WeekBudgetComponent extends LitElement {
|
||||
displayMode: DisplayMode = DisplayMode.BudgetView
|
||||
|
||||
budgets: Budget[] = []
|
||||
hasCreateError: boolean = false
|
||||
hasJoinError: boolean = false
|
||||
|
@ -17,7 +29,8 @@ class WeekBudgetComponent extends LitElement {
|
|||
return {
|
||||
budgets: { type: Array },
|
||||
hasCreateError: { type: Boolean },
|
||||
hasJoinError: { type: Boolean }
|
||||
hasJoinError: { type: Boolean },
|
||||
displayMode: { type: DisplayMode }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -26,6 +39,24 @@ class WeekBudgetComponent extends LitElement {
|
|||
h1 {
|
||||
color: var(--light-accent);
|
||||
}
|
||||
|
||||
hr {
|
||||
border: none;
|
||||
margin: 40px 20px;
|
||||
height: 1px;
|
||||
background-color: var(--shaded-bright);
|
||||
}
|
||||
|
||||
.actions {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-around;
|
||||
align-items: center;
|
||||
|
||||
/* SUPER BIG */
|
||||
border-radius: 10px;
|
||||
font-size: 400%;
|
||||
}
|
||||
`
|
||||
}
|
||||
|
||||
|
@ -137,23 +168,61 @@ class WeekBudgetComponent extends LitElement {
|
|||
storeToLocal(this.budgets)
|
||||
}
|
||||
|
||||
setDisplayMode(mode: DisplayMode) {
|
||||
this.displayMode = mode
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<h1>TinyBudget</h1>
|
||||
<budget-list
|
||||
@spendMoney=${this.onSpendMoney}
|
||||
@resetBudget=${this.onResetBudget}
|
||||
@renameBudget=${this.onRenameBudget}
|
||||
@removeBudget=${this.onRemoveBudget}
|
||||
.budgets=${this.budgets}
|
||||
></budget-list>
|
||||
<budget-add
|
||||
@createBudget=${this.onCreateBudget}
|
||||
@joinBudget=${this.onJoinBudget}
|
||||
.hasCreateError=${this.hasCreateError}
|
||||
.hasJoinError=${this.hasJoinError}
|
||||
></budget-add>
|
||||
const headingWithContent = (content: TemplateResult) => html`<h1>${content}TinyBudget</h1>`
|
||||
const back = html`
|
||||
<fa-button @click=${() => this.setDisplayMode(DisplayMode.BudgetView)}>
|
||||
<fa-icon name="arrow-left" alt="Go back"></fa-icon>
|
||||
</fa-button>
|
||||
`
|
||||
|
||||
switch (this.displayMode) {
|
||||
case DisplayMode.CreateView:
|
||||
return html`
|
||||
${headingWithContent(back)}
|
||||
<budget-create
|
||||
@createBudget=${this.onCreateBudget}
|
||||
.hasError=${this.hasCreateError}
|
||||
></budget-create>
|
||||
`
|
||||
|
||||
case DisplayMode.JoinView:
|
||||
return html`
|
||||
${headingWithContent(back)}
|
||||
<budget-join
|
||||
@joinBudget=${this.onJoinBudget}
|
||||
.hasError=${this.hasJoinError}
|
||||
></budget-join>
|
||||
`
|
||||
|
||||
default:
|
||||
return html`
|
||||
${headingWithContent(html``)}
|
||||
<budget-list
|
||||
@spendMoney=${this.onSpendMoney}
|
||||
@resetBudget=${this.onResetBudget}
|
||||
@renameBudget=${this.onRenameBudget}
|
||||
@removeBudget=${this.onRemoveBudget}
|
||||
.budgets=${this.budgets}
|
||||
></budget-list>
|
||||
|
||||
<hr />
|
||||
|
||||
<div class="actions">
|
||||
<fa-button @click=${() => this.setDisplayMode(DisplayMode.CreateView)}>
|
||||
<fa-icon name="plus" alt="Create new budget"></fa-icon>
|
||||
</fa-button>
|
||||
|
||||
<fa-button @click=${() => this.setDisplayMode(DisplayMode.JoinView)}>
|
||||
<fa-icon name="user-friends" alt="Join existing budget"></fa-icon>
|
||||
</fa-button>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
}
|
||||
|
||||
async _getBudget(uuid: string): Promise<Budget> {
|
||||
|
|
Reference in a new issue