Skip to content

Commit

Permalink
Guests can now be added and viewed.
Browse files Browse the repository at this point in the history
  • Loading branch information
Mondei1 committed Jun 11, 2023
1 parent bdf23af commit 9fc1e4d
Show file tree
Hide file tree
Showing 10 changed files with 343 additions and 75 deletions.
6 changes: 6 additions & 0 deletions public/locales/de/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,17 @@
"up": "hoch",
"down": "runter",
"delete": "löschen",
"clear_input": "Eingabe löschen",
"settings": "Einstellungen",
"settings_modal": {
"language": "Sprache auswählen",
"theme": "Farbschema ändern"
},
"first_name": "Vorname",
"last_name": "Nachname",
"additional_guests": "zusätzliche Gäste",
"add_guest": "Gast hinzufügen",
"add_guest_tip": "Tipp: Drücke Enter zum schnellen speichern.",
"setup": {
"welcome": "Einrichtung",
"text": "Zuerst musst du ein paar Fragen beantworten bevor es losgehen kann.",
Expand Down
6 changes: 6 additions & 0 deletions public/locales/en/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,17 @@
"up": "Up",
"down": "Down",
"delete": "Delete",
"clear_input": "Clear input",
"settings": "Settings",
"settings_modal": {
"language": "Select language",
"theme": "Change color schema"
},
"first_name": "First name",
"last_name": "Last name",
"additional_guests": "Additional guests",
"add_guest": "Add new guest",
"add_guest_tip": "Tip: Press Enter to save fast.",
"setup": {
"welcome": "Setup",
"text": "To create a new seating plan you have to answer a few questions first.",
Expand Down
53 changes: 49 additions & 4 deletions src/components/Database.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export enum DatabaseInfoKey {
}

export interface IGuest {
id: number,
id?: number,
firstName: string,
lastName: string,
additionalGuestAmount: number,
Expand Down Expand Up @@ -211,9 +211,11 @@ export async function getHighestSeatId(db: Database): Promise<number> {
}

export async function addGuest(db: Database, guest: IGuest): Promise<boolean> {
console.log("save ", guest);

try {
await db.execute("INSERT INTO guest (first_name, last_name, guests_amount, guests_checkedin, checkedin) VALUES ($1, $2, $3, $4, $5)",
[guest.firstName, guest.lastName, guest.additionalGuestAmount, guest.additionalGuestCheckedin, guest.checkedIn])
await db.execute("INSERT INTO participant (first_name, last_name, guests_amount, guests_checkedin, checkedin) VALUES ($1, $2, $3, $4, $5)",
[guest.firstName, guest.lastName, guest.additionalGuestAmount | 0, guest.additionalGuestCheckedin, guest.checkedIn])

return true
} catch (err) {
Expand All @@ -223,16 +225,59 @@ export async function addGuest(db: Database, guest: IGuest): Promise<boolean> {
}
}

function convertGuest(dbResult: any[]): Array<IGuest> {
let result = new Array<IGuest>(dbResult.length)
for (let i = 0; i < dbResult.length; i++) {
const element = dbResult[i];

result.push({
id: element.id,
firstName: element.first_name,
lastName: element.last_name,
additionalGuestAmount: element.guests_amount,
additionalGuestCheckedin: element.guests_checkedin,
checkedIn: element.checkedin === "true" ? true : false
})
}

return result
}

export async function getGuests(db: Database): Promise<Array<IGuest>> {
try {
return (await db.select("SELECT * FROM participant")) || []
let rawResult: any[] = await db.select("SELECT * FROM participant ORDER BY id")
if (rawResult === null || rawResult.length == 0) {
return []
}

return convertGuest(rawResult)
} catch (err) {
console.error("Couldn't get guests: ", err)

return []
}
}

/**
* Used to paginate the response.
*
* @param cursor: Where to start returning the next `amount` of results.
*/
export async function getGuestPage(db: Database, lastId: number, amount: number): Promise<Array<IGuest>> {
try {
let rawResult: any[] = await db.select("SELECT * FROM participant WHERE id > $1 ORDER BY id LIMIT $2", [lastId, amount])
if (rawResult === null || rawResult.length == 0) {
return []
}

return convertGuest(rawResult)
} catch (err) {
console.error("Couldn't paginate guests: ", err);

return []
}
}

export async function deleteGuest(db: Database, guestId: number): Promise<boolean> {
try {
await db.execute("DELETE FROM participant WHERE id = $1", [guestId])
Expand Down
135 changes: 135 additions & 0 deletions src/components/editor/GuestModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
import { Button, FormElement, Input, Modal, Text } from "@nextui-org/react"
import { TFunction } from "next-i18next"
import { UserAddIcon } from "../icons/UserAddIcon"
import { EraserIcon } from "../icons/EraserIcon"
import { IGuest } from "../Database"
import { KeyboardEventHandler, useState } from "react"

export type GuestModalProps = {
t: TFunction,
visible: boolean,
closeHandler: () => void,
addGuest: (guest: IGuest) => void
}

export const GuestModal: React.FC<GuestModalProps> = ({ t, closeHandler, visible, addGuest }) => {

const [firstName, setFirstName] = useState("")
const [lastName, setLastName] = useState("")
const [additionalGuests, setAdditionalGuests] = useState<number>(0)

function save() {
addGuest({
firstName,
lastName,
additionalGuestAmount: additionalGuests!,
additionalGuestCheckedin: 0,
checkedIn: false
})
clear()
}

function clear() {
setFirstName("")
setLastName("")
setAdditionalGuests(0)

document.getElementById("first-name-input")?.focus()
}

function triggerEnter(e: KeyboardEvent) {
if (e.code === "Enter") {
save()
}
}

function validateAdditionalGuests(value: number) {
if (value < 0) {
setAdditionalGuests(0)
return
}

setAdditionalGuests(value)
}

return (
<Modal
closeButton
aria-labelledby="add-new-guest"
open={visible}
onClose={closeHandler}
>
<Modal.Header>
<Text size={18}>{t("map.new_guest")}</Text>
</Modal.Header>

<Modal.Body>
<Input
id="first-name-input"
bordered
required
fullWidth
autoFocus
autoComplete="off"
color="primary"
size="lg"
fill="false"
label={t("first_name")!}
value={firstName}
onChange={(e) => setFirstName(e.target.value)}
// @ts-ignore: I don't care seriously.
onKeyDown={triggerEnter}
/>
<Input
bordered
fullWidth
color="primary"
size="lg"
value={lastName}
onChange={(e) => setLastName(e.target.value)}
label={t("last_name")!}

// @ts-ignore: Same as above.
onKeyDown={triggerEnter}
/>
<Input
clearable
bordered
fullWidth
color="primary"
size="lg"
type="number"
value={additionalGuests!}
onChange={(e) => { validateAdditionalGuests(Number.parseInt(e.target.value)) }}
label={t("additional_guests")!}

// @ts-ignore: Guess what
onKeyDown={triggerEnter}
/>
</Modal.Body>

<Modal.Footer>
<div className="grid gap-4 grid-cols-2">
<Button
auto
bordered
color="error"
icon={<EraserIcon />}
onPress={clear}
>
{t("clear_input")}
</Button>
<Button
auto
onPress={save}
icon={<UserAddIcon />}
>
{t("add_guest")}
</Button>
</div>

<Text small color="gray" className="pt-3">{t("add_guest_tip")}</Text>
</Modal.Footer>
</Modal>
)
}
57 changes: 46 additions & 11 deletions src/components/editor/GuestTable.tsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,46 @@
import { Button, Col, Input, Loading, Row, Spacer, Table, Text, Tooltip, User } from "@nextui-org/react"
import React, { Key, useState } from "react"
import { Button, Col, Input, Loading, Row, Spacer, Table, Text, Tooltip, User, useAsyncList } from "@nextui-org/react"
import React, { Key, useEffect, useState } from "react"
import { useTranslation } from "react-i18next"
import { IFloor, IGuest } from "../Database"
import { IFloor, IGuest, getGuestPage, getGuests } from "../Database"
import { IconButton } from "../IconButton"
import { DeleteIcon } from "../icons/DeleteIcon"
import { EditIcon } from "../icons/EditIcon"
import Database from "tauri-plugin-sql-api"

interface ISchematicColumn {
key: Key,
label: string
}

type GuestTableProps = {
guests: Array<IGuest>,
//guests: Array<IGuest>,
db: Database,
deleteGuest: (guestId: number) => void,
newGuest: (guest: IGuest) => void
}

export const GuestTable: React.FC<GuestTableProps> = ({ guests, deleteGuest, newGuest }) => {
export const GuestTable: React.FC<GuestTableProps> = ({ db, deleteGuest, newGuest }) => {
const { t } = useTranslation('common')

const [guests, setGuests] = useState<IGuest[]>([])

useEffect(() => {
getGuests(db).then(g => {
setGuests(g)
})
}, [])

// @ts-ignore
async function load({ signal, cursor }) {
return {
items: await getGuestPage(db, cursor || 0, 3),
cursor: (cursor || 0) + 3
}
}

// @ts-ignore
const list = useAsyncList({ load })

// Table structure
const columns: Array<ISchematicColumn> = [
{
Expand All @@ -40,25 +61,35 @@ export const GuestTable: React.FC<GuestTableProps> = ({ guests, deleteGuest, new
}
]


const renderCell = (guest: IGuest, columnKey: React.Key) => {
console.log("Render cell: ", guest);

// @ts-ignore
const cellValue: any = guest[columnKey];
switch (columnKey) {
case "level":
return (<Text>{cellValue}</Text>)
case "guests":
return (<Text>{guest.additionalGuestAmount}</Text>)
case "present":
return (<Text>{guest.additionalGuestCheckedin}</Text>)
case "name":
return (
<User squared text={guest.firstName} name={guest.firstName + " " + guest.lastName}>
<User
squared
text={guest.firstName}
name={guest.firstName + " " + guest.lastName}
bordered={guest.checkedIn}
color="success"
zoomed
>
<p></p>
</User>
);

case "actions":
return (
<Row justify="flex-end">
<Col css={{ d: "flex" }}>
<Col className="flex gap-4 ml-0">
<Tooltip
content={t("edit")}
>
Expand Down Expand Up @@ -97,13 +128,17 @@ export const GuestTable: React.FC<GuestTableProps> = ({ guests, deleteGuest, new
hideHeader={column.key == "actions"}
// @ts-ignore
width={column.key === "actions" ? "12px" : "auto"}
align={column.key === "actions" ? "center" : "start"}
align={column.key === "actions" ? "center" : "start"}
>
{column.label}
</Table.Column>
)}
</Table.Header>
<Table.Body items={guests}>
<Table.Body
items={list.items}
loadingState={list.loadingState}
onLoadMore={list.loadMore}
>
{(item) => (
<Table.Row key={item.id}>
{(columnKey) =>
Expand Down
3 changes: 0 additions & 3 deletions src/components/editor/Navbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { ArrowLeftIcon } from "../icons/ArrowLeftIcon";
import { LayerIcon } from "../icons/LayerIcon";
import { EditIcon } from "../icons/EditIcon";
import { UsersIcon } from "../icons/UsersIcon";
import { SearchIcon } from "../icons/SearchIcon";
import { useTranslation } from "react-i18next";
import { TFunction } from "next-i18next";
import { IFloor, getFloors } from "../Database";
Expand Down Expand Up @@ -59,8 +58,6 @@ export const EditorNavbar: React.FC<EditorNavbarProps> = ({ onEditGuests, onEdit
if (db !== null) {
getFloors(db).then(f => {
if (f !== null) {
console.log("Fetched floors: ", f)

// Fake floor that acts as settings.
let settingsFloor: IFloor = {
id: MAGICAL_SETTINGS_ID,
Expand Down
Loading

0 comments on commit 9fc1e4d

Please sign in to comment.