Skip to content

Commit

Permalink
chore: prettier
Browse files Browse the repository at this point in the history
  • Loading branch information
IObert committed Oct 15, 2024
1 parent 2252f9e commit 379e19d
Show file tree
Hide file tree
Showing 16 changed files with 113 additions and 114 deletions.
2 changes: 1 addition & 1 deletion CODE_OF_CONDUCT.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,4 +70,4 @@ members of the project's leadership.
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html

[homepage]: https://www.contributor-covenant.org
[homepage]: https://www.contributor-covenant.org
54 changes: 26 additions & 28 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,50 +8,49 @@
Twilio Mixologist is an application that allows you to solve the problem of long queues at stands at events. Attendees can order their coffee, smoothie or whatever you serve via Twilio-powered channels, Mixologists get all orders on a website that can be accessed via a tablet and once an order is done the attendee will be notified via the system to come and pick it up. No more queueing and efficient coffee ☕️ ordering! 🎉

If you want to learn more about how this project was started, check out the this blog post:
> [Serving Coffee with Twilio Programmable SMS and React](https://www.twilio.com/en-us/blog/serving-coffee-with-sms-and-react-html)

Different versions of this system have been used at events such as:

* [NDC Oslo](https://ndcoslo.com) 2016, 2017
* [CSSConf EU](https://2017.cssconf.eu/) && [JSConf EU](https://2017.jsconf.eu/) 2017
* [WeAreDevelopers World Congress](https://www.wearedevelopers.com/world-congress) 2023, 2024
* [Mobile World Congress Barcelona](https://www.mwcbarcelona.com/) 2023, 2024
* [Money 20/20](https://www.money2020.com/) 2023
* [Twilio SIGNAL](https://signal.twilio.com/) 2023, 2024
> [Serving Coffee with Twilio Programmable SMS and React](https://www.twilio.com/en-us/blog/serving-coffee-with-sms-and-react-html)
Different versions of this system have been used at events such as:

- [NDC Oslo](https://ndcoslo.com) 2016, 2017
- [CSSConf EU](https://2017.cssconf.eu/) && [JSConf EU](https://2017.jsconf.eu/) 2017
- [WeAreDevelopers World Congress](https://www.wearedevelopers.com/world-congress) 2023, 2024
- [Mobile World Congress Barcelona](https://www.mwcbarcelona.com/) 2023, 2024
- [Money 20/20](https://www.money2020.com/) 2023
- [Twilio SIGNAL](https://signal.twilio.com/) 2023, 2024

## Features

* Receive orders using [Twilio Messaging]
* Store orders and real-time synchronization them between back-end and front-end using [Twilio Sync]
* Easy dynamic application configuration using [Twilio Sync]
* Managing message threads using [Twilio Conversations]
* Permission management based on [Twilio Sync]
* Easy way to reset the application from the admin interface
* Support multiple events that happen in parallel
* Query for location in the queue as well as canceling the order as a user
* All combined into a single [NextJS](https://nextjs.org/) web application
- Receive orders using [Twilio Messaging]
- Store orders and real-time synchronization them between back-end and front-end using [Twilio Sync]
- Easy dynamic application configuration using [Twilio Sync]
- Managing message threads using [Twilio Conversations]
- Permission management based on [Twilio Sync]
- Easy way to reset the application from the admin interface
- Support multiple events that happen in parallel
- Query for location in the queue as well as canceling the order as a user
- All combined into a single [NextJS](https://nextjs.org/) web application

### Pending Features

- [ ] Integration with Segment
- [ ] Your suggestions

### Channels

The current [Twilio Channels] are:

* [WhatsApp][twilio whatsapp]
* [SMS][twilio messaging]

- [WhatsApp][twilio whatsapp]
- [SMS][twilio messaging]

## Setup

### Requirements

* [Node.js] version 20 or higher
* [pnpm]
* A Twilio account - [Sign up here](https://www.twilio.com/try-twilio)
- [Node.js] version 20 or higher
- [pnpm]
- A Twilio account - [Sign up here](https://www.twilio.com/try-twilio)

## Setup

Expand Down Expand Up @@ -136,13 +135,12 @@ All third party contributors acknowledge that any contributions they provide wil

## Icons Used

* [Mixologist Icons by Oliver Pitsch](https://www.smashingmagazine.com/2016/03/freebie-Mixologist-iconset-50-icons-eps-png-svg/)
* [Bar by BirVa Mehta from Noun Project](https://thenounproject.com/term/bar/1323725/)
- [Mixologist Icons by Oliver Pitsch](https://www.smashingmagazine.com/2016/03/freebie-Mixologist-iconset-50-icons-eps-png-svg/)
- [Bar by BirVa Mehta from Noun Project](https://thenounproject.com/term/bar/1323725/)

## License

MIT

MIT

[twilio console]: https://www.twilio.com/console
[twilio rest api]: https://www.twilio.com/docs/api/rest
Expand Down
12 changes: 6 additions & 6 deletions __tests__/e2e/browse-orders.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -165,18 +165,18 @@ test.describe("[mixologist]", () => {

await page.goto("/event/test-event/orders");

await page
.getByRole("button", { name: "Create a Manual Order" })
.click();
await page.getByRole("button", { name: "Create a Manual Order" }).click();
await page.getByPlaceholder("Attendee name").fill("Test Name");
await page.getByLabel("Order Item").click();
await page.getByLabel('Espresso', { exact: true }).click();
await page.getByPlaceholder("Without regular milk or similar...").fill("Test Notes");
await page.getByLabel("Espresso", { exact: true }).click();
await page
.getByPlaceholder("Without regular milk or similar...")
.fill("Test Notes");
await page
.getByRole("button", { name: "Create Order", exact: true })
.click();
await expect(
page.getByRole("button", { name: "Creating...", exact: true }),
).toBeVisible();
});
});
});
6 changes: 3 additions & 3 deletions src/app/api/[slug]/broadcast/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,9 @@ export async function POST(
listItem.data?.status === "queued" || listItem.data?.status === "ready",
);

queuedOrders.forEach(order => {
addMessageToConversation(order.data.key, message)
})
queuedOrders.forEach((order) => {
addMessageToConversation(order.data.key, message);
});

return new Response(null, { status: 201 });
} catch (e: any) {
Expand Down
41 changes: 22 additions & 19 deletions src/app/api/broadcast/route.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,18 @@


import { headers } from "next/headers";

import {
fetchSyncListItems,
addMessageToConversation
} from "@/lib/twilio";
import { fetchSyncListItems, addMessageToConversation } from "@/lib/twilio";
import { Privilege, getAuthenticatedRole } from "@/middleware";

export async function POST(request: Request) {
const headersList = headers();
const role = getAuthenticatedRole(headersList.get("Authorization") || "");
const headersList = headers();
const role = getAuthenticatedRole(headersList.get("Authorization") || "");

const hasPermissions = Privilege.ADMIN === role || Privilege.MIXOLOGIST === role;
if (!process.env.NEXT_PUBLIC_EVENTS_MAP || !process.env.NEXT_PUBLIC_ACTIVE_CUSTOMERS_MAP ) {
const hasPermissions =
Privilege.ADMIN === role || Privilege.MIXOLOGIST === role;
if (
!process.env.NEXT_PUBLIC_EVENTS_MAP ||
!process.env.NEXT_PUBLIC_ACTIVE_CUSTOMERS_MAP
) {
console.error("No config doc specified");
return new Response("No config doc specified", {
status: 500,
Expand All @@ -31,16 +30,20 @@ const headersList = headers();
);
}

const { event, message } = await request.json();
try {
const listItems = await fetchSyncListItems(event);
const queuedOrders = listItems.filter(listItem => (listItem.data?.status === 'queued' || listItem.data?.status === 'ready') && !listItem.data?.manual);

queuedOrders.forEach(order => {
addMessageToConversation(order.data.key, message);
})
return new Response(null, {status: 201})
const { event, message } = await request.json();
try {
const listItems = await fetchSyncListItems(event);
const queuedOrders = listItems.filter(
(listItem) =>
(listItem.data?.status === "queued" ||
listItem.data?.status === "ready") &&
!listItem.data?.manual,
);

queuedOrders.forEach((order) => {
addMessageToConversation(order.data.key, message);
});
return new Response(null, { status: 201 });
} catch (e: any) {
console.error(e);
return new Response(e.message, { status: 500, statusText: e.message });
Expand Down
2 changes: 1 addition & 1 deletion src/app/api/event/[slug]/stats/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export async function GET(
request: Request,
{ params }: { params: { slug: string } },
): Promise<Response> {
const headersList = headers();
const headersList = headers();
const role = getAuthenticatedRole(headersList.get("Authorization") || "");

if (role !== Privilege.ADMIN) {
Expand Down
15 changes: 12 additions & 3 deletions src/app/event/[slug]/orders/[terminal]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,23 @@ export default function TerminalPage({
const terminalId = Number(matchedGroups?.[1]),
terminalCount = Number(matchedGroups?.[2]);

if (!terminalRegex.test(terminal) || terminalId > terminalCount || terminalId === 0 || isNaN(terminalId) || isNaN(terminalCount)) {
if (
!terminalRegex.test(terminal) ||
terminalId > terminalCount ||
terminalId === 0 ||
isNaN(terminalId) ||
isNaN(terminalCount)
) {
return notFound();
}


return (
<main className="p-4 md:p-6 lg:p-8 space-y-8">
<OrdersInterface slug={params.slug} terminalId={terminalId} terminalCount={terminalCount} />
<OrdersInterface
slug={params.slug}
terminalId={terminalId}
terminalCount={terminalCount}
/>
</main>
);
}
7 changes: 1 addition & 6 deletions src/app/event/[slug]/orders/ordersList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,7 @@ import {
getOrderReadyReminderMessage,
} from "@/lib/templates";

import {
Check,
Trash2Icon,
BellRing,
UserCheck,
} from "lucide-react";
import { Check, Trash2Icon, BellRing, UserCheck } from "lucide-react";

export default function OrdersList({
ordersList,
Expand Down
54 changes: 25 additions & 29 deletions src/app/webhooks/conversations/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,13 +89,12 @@ export async function POST(request: Request) {
await sleep(2000);
const dataPolicy = templates.getDataPolicy(newEvent.selection.mode);
addMessageToConversation(conversationSid, dataPolicy);
const message =
await templates.getReadyToOrderMessage(
newEvent,
newEvent.selection.items,
newEvent.maxOrders,
true
);
const message = await templates.getReadyToOrderMessage(
newEvent,
newEvent.selection.items,
newEvent.maxOrders,
true,
);
addMessageToConversation(
conversationSid,
"",
Expand Down Expand Up @@ -136,13 +135,12 @@ export async function POST(request: Request) {
await sleep(2000);
const dataPolicy = templates.getDataPolicy(newEvent.selection.mode);
addMessageToConversation(conversationSid, dataPolicy);
const message =
await templates.getReadyToOrderMessage(
newEvent,
newEvent.selection.items,
newEvent.maxOrders,
true
);
const message = await templates.getReadyToOrderMessage(
newEvent,
newEvent.selection.items,
newEvent.maxOrders,
true,
);
addMessageToConversation(
conversationSid,
"",
Expand Down Expand Up @@ -184,13 +182,12 @@ export async function POST(request: Request) {
newEvent.welcomeMessage,
);
addMessageToConversation(conversationSid, welcomeBackMessage);
const message =
await templates.getReadyToOrderMessage(
newEvent,
newEvent.selection.items,
newEvent.maxOrders,
true
);
const message = await templates.getReadyToOrderMessage(
newEvent,
newEvent.selection.items,
newEvent.maxOrders,
true,
);
await updateOrCreateSyncMapItem(
NEXT_PUBLIC_ACTIVE_CUSTOMERS_MAP,
conversationSid,
Expand Down Expand Up @@ -235,13 +232,12 @@ export async function POST(request: Request) {
);

await sleep(500);
const message =
await templates.getReadyToOrderMessage(
newEvent,
newEvent.selection.items,
newEvent.maxOrders,
true
);
const message = await templates.getReadyToOrderMessage(
newEvent,
newEvent.selection.items,
newEvent.maxOrders,
true,
);
addMessageToConversation(
conversationSid,
"",
Expand Down Expand Up @@ -355,7 +351,7 @@ export async function POST(request: Request) {
event,
event.selection.items,
event.maxOrders,
false
false,
);
addMessageToConversation(
conversationSid,
Expand Down
2 changes: 1 addition & 1 deletion src/components/menu-item.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ export default function MenuItem({
className="m-2 mr-6"
/>
),
"Chocolate": (
Chocolate: (
<CoffeeCupIcon
width="3rem"
height="3rem"
Expand Down
2 changes: 1 addition & 1 deletion src/config/spellingMap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,5 +37,5 @@ export default {
"hot chocolate": "Hot Chocolate",
chocolate: "Hot Chocolate",
cocolate: "Hot Chocolate",
"soya": "soy",
soya: "soy",
} as SpellingMap;
15 changes: 8 additions & 7 deletions src/instrumentation.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
"use server";



export async function register() {
if (process.env.NEXT_RUNTIME === "nodejs") {
const { getVerifyService,getMessagingService,getSyncService,getConversationService} = await import("@/lib/twilio");
const { updateConfig } = await import("@/scripts/updateConfig")
const { createTwilioRes }= await import("@/scripts/createTwilioRes")
const {
getVerifyService,
getMessagingService,
getSyncService,
getConversationService,
} = await import("@/lib/twilio");
const { updateConfig } = await import("@/scripts/updateConfig");
const { createTwilioRes } = await import("@/scripts/createTwilioRes");

await checkIfAllEnvVarsAreSet();

Expand All @@ -21,8 +24,6 @@ export async function register() {
} catch (e: any) {
throw new Error(e.message);
}


}
}

Expand Down
6 changes: 1 addition & 5 deletions src/lib/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,11 +62,7 @@ export async function getOrderItemFromMessage(event: Event, message: string) {
(a, b) => b.shortTitle.length - a.shortTitle.length,
);
for (const item in sortedItems) {
if (
spellcheckedBody.includes(
sortedItems[item].shortTitle.toLowerCase(),
)
) {
if (spellcheckedBody.includes(sortedItems[item].shortTitle.toLowerCase())) {
orderItem = sortedItems[item];
break;
}
Expand Down
Loading

0 comments on commit 379e19d

Please sign in to comment.