Git Product home page Git Product logo

breezbook's Introduction

BreezBook

BreezBook is an open-source white-label booking app that can be used by customers to book appointments with a business. It will focus only on the booking experience, integrating with low-code tools like Airtable/SmartSuite etc to manage the remaining processes of the business.

Why BreezBook?

Existing booking apps like Acuity offer pretty vanilla booking experiences. Their forums are full of people asking for features that would delight their customers during the booking process.

BreezBook will focus hard on best-in-class booking experiences, and integrate with low-code tools like Airtable/SmartSuite etc. to manage the remaining processes of the business.

Features

  • White-label for multiple tenants
  • Services for a tenant
  • Multiple services for a tenant
  • Time slots
  • Accessories
  • Extra customer details specific to the service
  • Cart system for multiple bookings or services
  • Payment
  • Dynamic pricing
  • Custom themes
    • Automatic theme detection from the tenant's website

Tech Stack

  • Turborepo
  • TypeScript
  • SvelteKit
  • TailwindCSS + DaisyUI
  • Stripe
  • Vercel
  • Airtable
  • AI

What's inside?

This repo includes the following packages/apps:

Apps and Packages

  • @breezbook/booking: Booking app made with SvelteKit for customers to book appointments with the business
  • @breezbook/eslint-config: eslint configurations (includes eslint-config-next and eslint-config-prettier)
  • @breezbook/typescript-config: tsconfig.jsons used throughout the monorepo

Each package/app is 100% TypeScript.

Utilities

This Turborepo has some additional tools already setup for you:

Build

To build all apps and packages, run the following command:

pnpm build

Develop

To develop all apps and packages, run the following command:

pnpm dev

Remote Caching

Turborepo can use a technique known as Remote Caching to share cache artifacts across machines, enabling you to share build caches with your team and CI/CD pipelines.

By default, Turborepo will cache locally. To enable Remote Caching you will need an account with Vercel. If you don't have an account you can create one, then enter the following commands:

cd my-turborepo
npx turbo login

This will authenticate the Turborepo CLI with your Vercel account.

Next, you can link your Turborepo to your Remote Cache by running the following command from the root of your Turborepo:

npx turbo link

Useful Links

Learn more about the power of Turborepo:

breezbook's People

Contributors

mike-hogan avatar metehan-altuntekin avatar turbobot-temp avatar ianesling avatar

Stargazers

Beno avatar Sonny avatar Bogdan avatar  avatar Brendan Foxen avatar  avatar

Watchers

 avatar

Forkers

ianesling

breezbook's Issues

Web Components - Shadow DOM Tailwind style issue

I have configured the web-components workspace with Rollup to properly compute DaisyUI & Tailwind stylings.

When I use the web component without Shadow DOM, the styling applies to the whole page and that's not how we want it.

But when I add the component with a Shadow DOM, some of the stylings doesn't apply properly. Sizing, display, applies properly but there is a problem with especially the colors.

image

Add timezone support

Use case might be a zoom call booking between UK and Turkey.

Or east coast and west coast in the US

Order email price problem

In the emails that are sent to the customer when they make an order, if they order multiple bookings at once the pricing comes inaccurate and more than actual booking or even the order total.

image

Batch airtable pushes

Right now we're syncing to airtable one mutation at a time. And it seems to be taking around 15 seconds for each. This is surprising, but maybe it is due to the orchestration by Inngest?

In any case, we can make the sync process convert all new mutation events into airtable mutation events, and apply those changes in batch - or at least quickly one after another - taking rate limits into account. Then mark all those events as replicated. And we can make this process have concurrency of 1 based on tenantId + environmentId.

Also, while doing this, make each step return something useful to see in the Inngest console. Like what mutation event id it handled, and what airtable record ids it edited.

add totalling function

Given an order and its associated order lines, and calculated prices, product a total

Getting to paid booking

These are the tasks to get to the point that we can do an end to end booking:

  • create an appropriate instance of Order - (packages/core)
  • sum up the order total using calculateOrderTotal
  • send the order to the order placement endpoint - /api/:envId/:tenantId/orders
  • assuming 2xx response from the above, create a stripe payment intent by posting to /api/:envId/:tenantId/orders/:orderId/paymentIntent
  • using that stripe payment intent, present the stripe payment form and submit the results to stripe
  • #24
  • allow entry of coupon code (and action of that)

Getting Nat onboarded

  • migrate his tenant and configuration into the database
  • get push of basic data into airtable done
  • connect stripe webhooks to confirm order payment
  • connect his stripe account to his tenant
  • success page
  • cancellation flow
  • images for services
  • images for tenants
  • get locations into the model
  • #26
  • get thankYouUrl support into the app
  • endpoint to establish airtable refresh token (on per tenant-env basis)
  • Publish Nat's services and add-ons to his airtable
  • Investigate why service was not replicated to airtable, and make this robust
  • replicate cancellation events
  • fire airtable replication on booking, on payment, and on cancellation (wrapped endpoint?)
  • replicate booking cost into airtable
  • replicate booking add-ons into airtable. Add-on ids can't have dots in them, so redo the mapping in x.sql

Backend to support rescheduling

Customer will get a confirmation email, with a link to reschedule.

Clicking it will bring them to a reschedule page, that shows the beginning of the booking journey (i.e. the time and date selector), with their existing choice highlighted.

They can resume the entire booking experience from here i.e pick a new date and time, add/remove add-ons.

But the details about the service (car make, model, colour etc) remain the same - or do they?

And the details about the customer - name, email etc - remain the same. Or do they?

If there is a delta in the price, because there is dynamic pricing and the date is brought forward to a more expensive slot - or the reverse - an extra charge, or a partial refund is calculated and issued.

Nat's view:

They could even change service - from a small car wash to a large car wash, or vice versa.
They can change details of the car i.e. service details.
Delta in price is auto calculated, and either a payment taken or a partial refund issued.

------------- TECHNICAL NOTES, FEEL FREE TO IGNORE --------------

How to change the data in the database / airtable?

One option is to cancel the original booking and make a new one. This might tho issue a cancellation email and a booking email. Is that a bad thing? How would we want this change communicated to the user - a specific reschedule email template?

The other option is to update the booking in place. What event will then be triggered to cause outbound comms to the user? I guess we could just update the order in place, AND attach a reschedule event.

And the same should happen during cancellation. That update event can trigger customer comms, and other actions.

Snag list following user test

My wife used the app on her phone this morning. Some tweaks follow:

  • She didn't like the warning about this bring a preview release. Lets get rid of that now.
  • The Year input has a 0 in it my default. When she tabbed into it, she had to clear the 0 before typing the year. She did not like that - can we make this empty?
  • She typed a non-email into the Email input on the Customer Details and when she clicked "Next", it failed to place the order. We need to validate that this is an email address.
  • She said calling this section "Customer Details" is unfriendly, it would be better if it was "Your Details"
  • On the "Checkout" page, the input box and button combo for Coupon Code scrolled off the right hand side of her screen. Can we make it fit?
  • When she progressed to the section called "Details" - after add-ons - the input box for Make scrolled off the top of the screen and she had to scroll back. Lets make sure that this input is visible, and it has focus.
  • When she clicked next on Customer Details, the Stripe element did not load. This happens some times because our wifi is so terrible here.
  • When using the success_return_url feature, I get redirected to https://www.google.com/?payment_intent=pi_3PEqV9H&payment_intent_client_secret=pi_3PEqV9HMCUK&redirect_status=succeeded - we need to drop these stripe query params
  • The placeholder for the "First Line Of Address" is too long for the input box. It looked a bit imperfect. Should we drop placeholders and use small-font descriptions under the label?
  • The list of add-ons is too long, and badly named. She had no idea what those things are. She wanted a shorter list, and she wanted better names.

Second customer booking dies on insert of customer form

This code is trying to update a customer if one exists with the given email, otherwise create a new customer with the given id.

Then it tries to upsert the customer form - by customer id. So if the customer already exists, it will not have the id coming into the method. Need to find a way to fix:

function upsertCustomerAsMutations(tenantEnvironment: TenantEnvironment, order: Order, tenantSettings: TenantSettings): Mutation[] {
	const tenant_id = tenantEnvironment.tenantId.value;
	const environment_id = tenantEnvironment.environmentId.value;
	const email = order.customer.email.value;
	const upserts: Mutation[] = [
		upsertCustomer(
			{
				id: order.customer.id.value,
				email,
				first_name: order.customer.firstName,
				last_name: order.customer.lastName,
				environment_id,
				tenants: {
					connect: { tenant_id }
				}
			},
			{
				first_name: order.customer.firstName,
				last_name: order.customer.lastName,
				tenant_id
			},
			{ tenant_id_environment_id_email: { tenant_id, environment_id, email } }
		)
	];
	if (tenantSettings.customerFormId && order.customer.formData) {
		const create: Prisma.customer_form_valuesCreateArgs['data'] = {
			environment_id: tenantEnvironment.environmentId.value,
			tenants: {
				connect: { tenant_id }
			},
			form_values: order.customer.formData,
			customers: {
				connect: {
					tenant_id_environment_id_email: { tenant_id, environment_id, email }
				}
			}
		};
		const update: Prisma.customer_form_valuesUpdateArgs['data'] = {
			form_values: order.customer.formData
		};
		const where: Prisma.customer_form_valuesWhereUniqueInput = {
			tenant_id_environment_id_customer_id: { tenant_id, environment_id, customer_id: order.customer.id.value }
		};

		upserts.push(upsertCustomerFormValues(create, update, where));
	}
	return upserts;
}

Support versioning in Forms

A user will amend custom form definitions thru the life of their business. Lets say they add a mandatory phone number. Old customers will not have a phone number, new customers will require one, and everything needs to continue to work.

get location support into the Booking App

Locations is a feature of the service booking logic. It hasn't been implemented yet into the Booking App.

How can we add the location support to the Booking App?

Location feature in the Booking App raises a lot of problems even just for the booking flow:

  1. Where to place the checkout route and it's pages? Inside location or somewhere else?
  2. How to make the stores work with multiple locations and services?
  3. Can a customer book from services from different locations in the same order?
  4. Where to locate cancelation page? Nested in location or not?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.