Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bookings emails #712

Merged
merged 43 commits into from
Feb 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
b63fc80
added emails HTML for emails in bookings
hunar1312 Jan 23, 2024
b7a736f
added emails HTML for emails in bookings
hunar1312 Jan 23, 2024
a82df03
Merge branch 'bookings' into bookings-emails
hunar1312 Jan 23, 2024
64381f3
Merge branch 'main' into bookings-emails
DonKoko Jan 24, 2024
0e30503
feat: disable ui in workspace switching
melsonic Jan 30, 2024
c78b1fd
fixing issue with broken string
DonKoko Jan 29, 2024
dde84c2
increasing limit of connections
DonKoko Jan 29, 2024
d0efe9f
integrating sentry
DonKoko Jan 29, 2024
0c1e910
mend
DonKoko Jan 29, 2024
0e491cd
adjusting SENTRY_DSN, making it not required
DonKoko Jan 29, 2024
04d75f9
remove sentry client side logging
DonKoko Jan 29, 2024
295e2e4
Fixed wrong var name
DonKoko Jan 29, 2024
1a7f349
use different build function depending on weather sentry is enabled
DonKoko Jan 29, 2024
51268a3
mend
DonKoko Jan 29, 2024
f250431
replacing custom integration with sentry gh action
DonKoko Jan 29, 2024
ae676ff
commenting out sourcemap part of deploy action to test if sentry even…
DonKoko Jan 29, 2024
844f866
mend
DonKoko Jan 29, 2024
5d34f79
replacing database_url with direct_url for pg-boss
DonKoko Jan 29, 2024
484b2c4
run sentry only on production
DonKoko Jan 30, 2024
e8c977b
initializing sentry with initial data for user
DonKoko Jan 30, 2024
40e0bb4
small adjustment
DonKoko Jan 30, 2024
c07fa9e
revert sentry changes
DonKoko Jan 30, 2024
219e384
fixing how state is being handled
DonKoko Jan 31, 2024
6cfa560
Merge branch 'main' into disable-ui-switch-workspace
DonKoko Jan 31, 2024
aa39393
resolved feeedback points
hunar1312 Jan 31, 2024
5ead459
resolved feedback points
hunar1312 Jan 31, 2024
8e1b686
Merge branch 'bookings-emails' of https://github.com/Shelf-nu/shelf.n…
hunar1312 Jan 31, 2024
e7a2659
Merge branch 'main' into disable-ui-switch-workspace
DonKoko Jan 31, 2024
6ac5ce9
centered the spinner
melsonic Jan 31, 2024
a0f8027
Merge branch 'main' into disable-ui-switch-workspace
DonKoko Feb 1, 2024
b2b907f
small fixes
DonKoko Feb 1, 2024
6068e5c
Merge branch 'main' into bookings-emails
DonKoko Feb 1, 2024
de83304
made sidebar items non interactive during workspace switching
melsonic Feb 1, 2024
deae420
updates to give more data to booking emails for a nicer footer
DonKoko Feb 1, 2024
b99ae9d
made sidebar items non interactive during workspace switching
melsonic Feb 1, 2024
89878eb
made sidebar items transparent during workspace switching
melsonic Feb 1, 2024
729ebae
Merge branch 'main' into disable-ui-switch-workspace
melsonic Feb 1, 2024
63c1c08
Merge pull request #710 from Shelf-nu/update-node
DonKoko Feb 1, 2024
95ae1bd
tailwind and formatting fixes
DonKoko Feb 1, 2024
8325881
Merge pull request #711 from Shelf-nu/tailwind-and-formatting-fixes
DonKoko Feb 1, 2024
69b3ae6
Merge branch 'main' into disable-ui-switch-workspace
DonKoko Feb 1, 2024
e082366
Merge pull request #702 from melsonic/disable-ui-switch-workspace
DonKoko Feb 1, 2024
07707cd
Merge branch 'main' into bookings-emails
DonKoko Feb 2, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions app/atoms/switching-workspace.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { atom } from "jotai";

export const switchingWorkspaceAtom = atom(true);
4 changes: 2 additions & 2 deletions app/components/booking/booking-assets-column.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -121,15 +121,15 @@ const ListAssetContent = ({ item }: { item: AssetWithBooking }) => {
<Td className="w-full p-0 md:p-0">
<div className="flex justify-between gap-3 p-4 md:justify-normal md:px-6">
<div className="flex items-center gap-3">
<div className="flex h-12 w-12 items-center justify-center">
<div className="flex size-12 items-center justify-center">
<AssetImage
asset={{
assetId: item.id,
mainImage: item.mainImage,
mainImageExpiration: item.mainImageExpiration,
alt: item.title,
}}
className="h-full w-full rounded-[4px] border object-cover"
className="size-full rounded-[4px] border object-cover"
/>
</div>
<div className="flex flex-row items-center gap-2 md:flex-col md:items-start md:gap-0">
Expand Down
2 changes: 1 addition & 1 deletion app/components/booking/delete-booking.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export const DeleteBooking = ({
<AlertDialogContent>
<AlertDialogHeader>
<div className="mx-auto md:m-0">
<span className="flex h-12 w-12 items-center justify-center rounded-full bg-error-50 p-2 text-error-600">
<span className="flex size-12 items-center justify-center rounded-full bg-error-50 p-2 text-error-600">
<TrashIcon />
</span>
</div>
Expand Down
2 changes: 1 addition & 1 deletion app/components/booking/remove-asset-from-booking.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ export const RemoveAssetFromBooking = ({ asset }: { asset: Asset }) => {
<AlertDialogContent>
<AlertDialogHeader>
<div className="mx-auto md:m-0">
<span className="flex h-12 w-12 items-center justify-center rounded-full bg-error-50 p-2 text-error-600">
<span className="flex size-12 items-center justify-center rounded-full bg-error-50 p-2 text-error-600">
<TrashIcon />
</span>
</div>
Expand Down
10 changes: 9 additions & 1 deletion app/components/layout/sidebar/bottom.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { useState } from "react";
import type { User } from "@prisma/client";
import { Form } from "@remix-run/react";
import { useAtom } from "jotai";
import { switchingWorkspaceAtom } from "~/atoms/switching-workspace";
import { ChevronRight, QuestionsIcon } from "~/components/icons";
import { CrispButton } from "~/components/marketing/crisp";
import { Button } from "~/components/shared";
Expand All @@ -20,9 +22,15 @@ interface Props {

export default function SidebarBottom({ user }: Props) {
const [dropdownOpen, setDropdownOpen] = useState(false);
const [workspaceSwitching] = useAtom(switchingWorkspaceAtom);

return (
<div className="bottom gap-2">
<div
className={tw(
"bottom gap-2",
workspaceSwitching ? "pointer-events-none" : ""
)}
>
<DropdownMenu modal={false}>
<DropdownMenuTrigger
onClick={() => setDropdownOpen((prev) => !prev)}
Expand Down
15 changes: 11 additions & 4 deletions app/components/layout/sidebar/menu-items.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { FetcherWithComponents } from "@remix-run/react";
import { NavLink, useLoaderData, useLocation } from "@remix-run/react";
import { motion } from "framer-motion";
import { useAtom } from "jotai";
import { switchingWorkspaceAtom } from "~/atoms/switching-workspace";
import { SwitchIcon } from "~/components/icons/library";
import { ControlledActionButton } from "~/components/shared/controlled-action-button";
import { useMainMenuItems } from "~/hooks/use-main-menu-items";
Expand All @@ -18,6 +19,7 @@ const MenuItems = ({ fetcher }: { fetcher: FetcherWithComponents<any> }) => {
const location = useLocation();
/** We need to do this becasue of a special way we handle the bookings link that doesnt allow us to use NavLink currently */
const isBookingsRoute = location.pathname.includes("/bookings");
const [workspaceSwitching] = useAtom(switchingWorkspaceAtom);

return (
<div className="flex h-full flex-col">
Expand All @@ -29,7 +31,8 @@ const MenuItems = ({ fetcher }: { fetcher: FetcherWithComponents<any> }) => {
className={({ isActive }) =>
tw(
"my-1 flex items-center gap-3 rounded px-3 py-2.5 text-[16px] font-semibold text-gray-700 transition-all duration-75 hover:bg-primary-50 hover:text-primary-600",
isActive ? "active bg-primary-50 text-primary-600" : ""
isActive ? "active bg-primary-50 text-primary-600" : "",
workspaceSwitching ? "pointer-events-none" : ""
)
}
to={"/admin-dashboard/users"}
Expand Down Expand Up @@ -77,6 +80,7 @@ const MenuItems = ({ fetcher }: { fetcher: FetcherWithComponents<any> }) => {
"data-test-id": `${item.label.toLowerCase()}SidebarMenuItem`,
onClick: toggleMobileNav,
title: item.label,
disabled: workspaceSwitching,
className: tw(
"my-1 flex items-center gap-3 rounded border-0 bg-transparent px-3 text-left text-[16px] font-semibold text-gray-700 transition-all duration-75 hover:bg-primary-50 hover:text-primary-600",
canUseBookings
Expand All @@ -95,7 +99,8 @@ const MenuItems = ({ fetcher }: { fetcher: FetcherWithComponents<any> }) => {
className={({ isActive }) =>
tw(
"my-1 flex items-center gap-3 rounded px-3 py-2.5 text-[16px] font-semibold text-gray-700 transition-all duration-75 hover:bg-primary-50 hover:text-primary-600",
isActive ? "active bg-primary-50 text-primary-600" : ""
isActive ? "active bg-primary-50 text-primary-600" : "",
workspaceSwitching ? "pointer-events-none" : ""
)
}
to={item.to}
Expand Down Expand Up @@ -131,7 +136,8 @@ const MenuItems = ({ fetcher }: { fetcher: FetcherWithComponents<any> }) => {
className={({ isActive }) =>
tw(
"my-1 flex items-center gap-3 rounded px-3 py-2.5 text-[16px] font-semibold text-gray-700 transition-all duration-75 hover:bg-primary-50 hover:text-primary-600",
isActive ? "active bg-primary-50 text-primary-600" : ""
isActive ? "active bg-primary-50 text-primary-600" : "",
workspaceSwitching ? "pointer-events-none" : ""
)
}
to={item.to}
Expand Down Expand Up @@ -159,7 +165,8 @@ const MenuItems = ({ fetcher }: { fetcher: FetcherWithComponents<any> }) => {
<button
type="submit"
className={tw(
"crisp-btn mt-1 flex w-full items-center gap-3 rounded px-3 py-2.5 text-[16px] font-semibold text-gray-700 transition-all duration-75 hover:bg-primary-50 hover:text-primary-600"
"crisp-btn mt-1 flex w-full items-center gap-3 rounded px-3 py-2.5 text-[16px] font-semibold text-gray-700 transition-all duration-75 hover:bg-primary-50 hover:text-primary-600",
workspaceSwitching ? "pointer-events-none" : ""
)}
>
<i className="icon pl-[2px] text-gray-500">
Expand Down
14 changes: 13 additions & 1 deletion app/components/layout/sidebar/organization-select.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import { useEffect } from "react";
import { useFetcher, useLoaderData } from "@remix-run/react";
import { useAtom } from "jotai";
import { switchingWorkspaceAtom } from "~/atoms/switching-workspace";
import {
Select,
SelectContent,
Expand All @@ -11,13 +14,22 @@ import { Button } from "~/components/shared";
import { Image } from "~/components/shared/image";
import ProfilePicture from "~/components/user/profile-picture";
import type { loader } from "~/routes/_layout+/_layout";
import { tw } from "~/utils";
import { isFormProcessing, tw } from "~/utils";

export const OrganizationSelect = () => {
const { organizations, currentOrganizationId } =
useLoaderData<typeof loader>();
const fetcher = useFetcher();

/** We track if the fetcher is submitting */
const isProcessing = isFormProcessing(fetcher.state);
const [_workspaceSwitching, setWorkspaceSwitching] = useAtom(
switchingWorkspaceAtom
);
useEffect(() => {
setWorkspaceSwitching(isProcessing);
}, [isProcessing, setWorkspaceSwitching]);

return (
<fetcher.Form
action={"/api/user/change-current-organization"}
Expand Down
9 changes: 5 additions & 4 deletions app/components/layout/sidebar/sidebar.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { useRef, useState } from "react";
import { Link, useFetcher, useLoaderData } from "@remix-run/react";
import { useAtom } from "jotai";
import { switchingWorkspaceAtom } from "~/atoms/switching-workspace";
import { ScanQRIcon, ShelfTypography } from "~/components/icons/library";

import {
useMediaStream,
useStopMediaStream,
Expand Down Expand Up @@ -32,7 +32,6 @@ export default function Sidebar() {
const handleScannerClose = () => {
stopMediaStream();
setShowScanner(false);
// window.location.reload();
};

/** We use optimistic UI for folding of the sidebar
Expand All @@ -46,6 +45,8 @@ export default function Sidebar() {
optimisticMinimizedSidebar =
sidebarFetcher.formData.get("minimizeSidebar") === "open";
}
const [workspaceSwitching] = useAtom(switchingWorkspaceAtom);

return (
<>
{/* this component is named sidebar as of now but also serves as a mobile navigation header in mobile device */}
Expand Down Expand Up @@ -106,12 +107,12 @@ export default function Sidebar() {
<div className="">
<OrganizationSelect key={currentOrganizationId} />
</div>
<div className="flex-1">
<div className={tw("flex-1", workspaceSwitching ? "opacity-50" : "")}>
<MenuItems fetcher={sidebarFetcher} />
</div>
</div>

<div className="mt-auto">
<div className={tw("mx-auto", workspaceSwitching ? "opacity-50" : "")}>
<SidebarBottom
isSidebarMinimized={optimisticMinimizedSidebar}
user={user}
Expand Down
155 changes: 155 additions & 0 deletions app/emails/bookings-updates-template.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
import {
Button,
Html,
Text,
Img,
Link,
Head,
render,
Container,
Heading,
} from "@react-email/components";
import type { ClientHint } from "~/modules/booking/types";
import { getDateTimeFormatFromHints } from "~/utils/client-hints";
import { SERVER_URL } from "~/utils/env";
import { styles } from "./styles";
import type { BookingForEmail } from "./types";

interface Props {
heading: string;
booking: BookingForEmail;
assetCount: number;
hints: ClientHint;
hideViewButton?: boolean;
}

export function BookingUpdatesEmailTemplate({
booking,
heading,
hints,
assetCount,
hideViewButton = false,
}: Props) {
const fromDate = getDateTimeFormatFromHints(hints, {
dateStyle: "short",
timeStyle: "short",
}).format(booking.from as Date);
const toDate = getDateTimeFormatFromHints(hints, {
dateStyle: "short",
timeStyle: "short",
}).format(booking.to as Date);
return (
<Html>
<Head>
<title>Bookings update from Shelf.nu</title>
</Head>

<Container
style={{ padding: "32px", textAlign: "center", maxWidth: "60%" }}
>
<div
style={{
display: "flex",
justifyContent: "center",
alignItems: "center",
marginBottom: "32px",
}}
>
<Img
src="cid:shelf-logo"
alt="Shelf's logo"
width="100"
height="32"
style={{ margin: "0 auto" }}
/>
</div>
<div style={{ margin: "32px" }}>
<Heading as="h1" style={{ ...styles.h1 }}>
{heading}
</Heading>
<Heading as="h2" style={{ ...styles.h2 }}>
{booking.name} | {assetCount}{" "}
{assetCount === 1 ? "asset" : "assets"}
</Heading>
<p style={{ ...styles.p }}>
<span style={{ color: "#101828", fontWeight: "600" }}>
Custodian:
</span>{" "}
{`${booking.custodianUser?.firstName} ${booking.custodianUser?.lastName}` ||
booking.custodianTeamMember?.name}
</p>
<p style={{ ...styles.p }}>
<span style={{ color: "#101828", fontWeight: "600" }}>From:</span>{" "}
{fromDate}
</p>
<p style={{ ...styles.p }}>
<span style={{ color: "#101828", fontWeight: "600" }}>To:</span>{" "}
{toDate}
</p>
</div>

{!hideViewButton && (
<Button
href={`${SERVER_URL}/bookings/${booking.id}`}
style={{
...styles.button,
textAlign: "center",
marginBottom: "32px",
}}
>
View booking in app
</Button>
)}

<Text style={{ fontSize: "14px", color: "#344054" }}>
This email was sent to{" "}
<Link
style={{ color: "#EF6820" }}
href={`mailto:${booking.custodianUser!.email}`}
>
{booking.custodianUser!.email}
</Link>{" "}
because it is part of the Shelf workspace.
<span style={{ color: "#101828", fontWeight: "600" }}>
{booking.organization.name}
</span>
. If you think you weren’t supposed to have received this email please{" "}
<Link
style={{ color: "#344054", textDecoration: "underline" }}
href={`mailto:${booking.organization.owner.email}`}
>
contact the owner
</Link>{" "}
of the workspace.
</Text>
<Text
style={{ marginBottom: "32px", fontSize: "14px", color: "#344054" }}
>
{" "}
© 2024 Shelf.nu
</Text>
</Container>
</Html>
);
}

/*
*The HTML content of an email will be accessed by a server file to send email,
we cannot import a TSX component in a server file so we are exporting TSX converted to HTML string using render function by react-email.
*/
export const bookingUpdatesTemplateString = ({
booking,
heading,
assetCount,
hints,
hideViewButton = false,
}: Props) =>
render(
<BookingUpdatesEmailTemplate
booking={booking}
heading={heading}
assetCount={assetCount}
hints={hints}
hideViewButton={hideViewButton}
/>
);
Loading
Loading