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

Embedded Portal Flow #19

Merged
merged 12 commits into from
Apr 5, 2024
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
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
50 changes: 47 additions & 3 deletions app/(authenticated)/embedded-portal/[spaceId]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,13 @@
import VisitSpaceForm from "@/components/shared/visit-space-form";
import EmbeddedPortal from "@/app/(authenticated)/embedded-portal/embedded-portal";
import { FlatfileService } from "@/lib/services/flatfile";
import { SpaceService } from "@/lib/services/space";
import {
EMBEDDED_PORTAL_ITEM,
PROJECT_ONBOARDING_INITIAL_STEPS,
} from "@/lib/workflow-constants";
import { redirect } from "next/navigation";
import HeaderContent from "@/app/(authenticated)/project-onboarding/header-content";
import { Step } from "@/components/shared/step-list";

export default async function Page({
params,
Expand All @@ -7,11 +16,46 @@ export default async function Page({
spaceId: string;
};
}) {
const steps: Step[] = [
{ ...PROJECT_ONBOARDING_INITIAL_STEPS[0], status: "complete" },
{ ...PROJECT_ONBOARDING_INITIAL_STEPS[1], status: "current" },
];
gaelyn marked this conversation as resolved.
Show resolved Hide resolved

const spaceId = params.spaceId;
const space = await SpaceService.getSpace({
id: spaceId,
});
if (!space) {
redirect("/embedded-portal");
}
const flatfileSpace = await FlatfileService.getSpace({
flatfileSpaceId: space.flatfileSpaceId,
});

Comment on lines +14 to +22
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add error handling for asynchronous operations to prevent unhandled promise rejections and improve user experience in case of errors.

+  }).catch(error => {
+    console.error("Failed to fetch space data:", error);
+    redirect("/error");
+  });
+
+  if (!space) {
+    redirect("/embedded-portal");
+  } else {
+    const flatfileSpace = await FlatfileService.getSpace({
+      flatfileSpaceId: space.flatfileSpaceId,
+    }).catch(error => {
+      console.error("Failed to fetch Flatfile space data:", error);
+      redirect("/error");
+    });
+
+    if (!flatfileSpace) {
+      redirect("/error");
+    }
+  }

Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
const space = await SpaceService.getSpace({
id: spaceId,
});
if (!space) {
redirect("/embedded-portal");
}
const flatfileSpace = await FlatfileService.getSpace({
flatfileSpaceId: space.flatfileSpaceId,
});
const space = await SpaceService.getSpace({
id: spaceId,
}).catch(error => {
console.error("Failed to fetch space data:", error);
redirect("/error");
});
if (!space) {
redirect("/embedded-portal");
} else {
const flatfileSpace = await FlatfileService.getSpace({
flatfileSpaceId: space.flatfileSpaceId,
}).catch(error => {
console.error("Failed to fetch Flatfile space data:", error);
redirect("/error");
});
if (!flatfileSpace) {
redirect("/error");
}
}

return (
<div>
<VisitSpaceForm spaceId={spaceId} />
<div className="space-y-6">
<HeaderContent item={EMBEDDED_PORTAL_ITEM} steps={steps} />
<div className="text-white">
<p className="text-2xl mb-8 md:max-w-lg">
Your embedded Flatfile space is configured and ready for import. 🎉
</p>
<div className="flex flex-col md:flex-row justify-between lg:justify-start lg:space-x-12 space-y-12 md:space-y-0">
<div className="md:max-w-md">
<p className="font-semibold mb-4">Launch Flatfile</p>
<p>Launch Flatfile via the “Import" button below.</p>
<p>
Use the Sidebar in the embedded application to guide you through
the import process!
</p>
<div className="mt-8">
<EmbeddedPortal
flatfileSpaceId={flatfileSpace.id}
flatfileSpaceAccessToken={flatfileSpace.accessToken as string}
/>
</div>
</div>
</div>
</div>
</div>
gaelyn marked this conversation as resolved.
Show resolved Hide resolved
gaelyn marked this conversation as resolved.
Show resolved Hide resolved
);
}
56 changes: 56 additions & 0 deletions app/(authenticated)/embedded-portal/embedded-portal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
"use client";

import { useState } from "react";
import { ISpace, initializeFlatfile } from "@flatfile/react";
import { Button } from "@/components/ui/button";
import {
ArrowsPointingInIcon,
ArrowsPointingOutIcon,
} from "@heroicons/react/24/outline";

export default function EmbeddedPortal({
flatfileSpaceId,
flatfileSpaceAccessToken,
}: {
flatfileSpaceId: string;
flatfileSpaceAccessToken: string;
}) {
const [showSpace, setShowSpace] = useState(false);

const spaceProps: ISpace = {
space: {
id: flatfileSpaceId,
accessToken: flatfileSpaceAccessToken,
},
namespace: process.env.NEXT_PUBLIC_FLATFILE_NAMESPACE as string,
environmentId: process.env.NEXT_PUBLIC_FLATFILE_ENVIRONMENT_ID as string,
};
const { Space, OpenEmbed } = initializeFlatfile({
...spaceProps,
closeSpace: {
operation: "contacts:submit",
onClose: () => setShowSpace(false),
},
});

const onOpenSpace = async () => {
setShowSpace(!showSpace);
OpenEmbed();
};
Comment on lines +46 to +49
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider adding error handling for the onOpenSpace function to gracefully manage any exceptions that might occur during the operation.

  const onOpenSpace = async () => {
    try {
      setShowSpace(!showSpace);
      OpenEmbed();
    } catch (error) {
      console.error("Error opening Flatfile space:", error);
      // Handle error appropriately
    }
  };

Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
const onOpenSpace = async () => {
setShowSpace(!showSpace);
OpenEmbed();
};
const onOpenSpace = async () => {
try {
setShowSpace(!showSpace);
OpenEmbed();
} catch (error) {
console.error("Error opening Flatfile space:", error);
// Handle error appropriately
}
};


return (
<div className="content">
<div>
<Button className="contrast" onClick={onOpenSpace}>
{showSpace ? "Close Portal" : "Import Data"}
{showSpace ? (
<ArrowsPointingInIcon className="w-4 h-4 ml-2" />
) : (
<ArrowsPointingOutIcon className="w-4 h-4 ml-2" />
)}
</Button>
{showSpace && <Space />}
</div>
</div>
);
}
31 changes: 20 additions & 11 deletions app/(authenticated)/embedded-portal/page.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,22 @@
import { WorkflowType } from "@/lib/workflow-type";
import CreateSpaceForm from "@/components/shared/create-space-form";

export default function Page() {
return (
<div>
<CreateSpaceForm
workflowType={WorkflowType.Embed}
spaceName={"Embedded Portal"}
/>
</div>
);
import { SpaceService } from "@/lib/services/space";
import invariant from "ts-invariant";
import { getServerSession } from "@/lib/get-server-session";
import SetupSpace from "@/app/(authenticated)/project-onboarding/setup-space";
import { redirect } from "next/navigation";

export default async function Page() {
const session = await getServerSession();
invariant(session?.user, "User must be logged in");

const space = await SpaceService.getSpaceForWorkflow({
userId: session.user.id,
workflowType: WorkflowType.Embed,
});
Comment on lines +16 to +19
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add error handling for the asynchronous operation to improve robustness and user experience in case of errors.

  const space = await SpaceService.getSpaceForWorkflow({
    userId: session.user.id,
    workflowType: WorkflowType.Embed,
  }).catch(error => {
    console.error("Failed to fetch space data:", error);
    // Redirect to an error page or show an error message
    redirect("/error");
  });

Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
const space = await SpaceService.getSpaceForWorkflow({
userId: session.user.id,
workflowType: WorkflowType.Embed,
});
const space = await SpaceService.getSpaceForWorkflow({
userId: session.user.id,
workflowType: WorkflowType.Embed,
}).catch(error => {
console.error("Failed to fetch space data:", error);
// Redirect to an error page or show an error message
redirect("/error");
});


if (space) {
redirect(`/embedded-portal/${space.id}`);
}
Comment on lines +16 to +23
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider wrapping the space fetching and redirection logic in a try-catch block to handle potential errors more gracefully.

+ try {
  const space = await SpaceService.getSpaceForWorkflow({
    userId: session.user.id,
    workflowType: WorkflowType.Embed,
  });

  if (space) {
    redirect(`/embedded-portal/${space.id}`);
  }
+ } catch (error) {
+   console.error("Error fetching space or redirecting:", error);
+   // Redirect to an error page or show an error message
+   redirect("/error");
+ }

Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
const space = await SpaceService.getSpaceForWorkflow({
userId: session.user.id,
workflowType: WorkflowType.Embed,
});
if (space) {
redirect(`/embedded-portal/${space.id}`);
}
try {
const space = await SpaceService.getSpaceForWorkflow({
userId: session.user.id,
workflowType: WorkflowType.Embed,
});
if (space) {
redirect(`/embedded-portal/${space.id}`);
}
} catch (error) {
console.error("Error fetching space or redirecting:", error);
// Redirect to an error page or show an error message
redirect("/error");
}


return <SetupSpace workflowType={WorkflowType.Embed} />;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Validate the logic for fetching space data and redirecting or rendering components based on the existence of a space. Consider adding error handling for the asynchronous operations involved.

  const space = await SpaceService.getSpaceForWorkflow({
    userId: session.user.id,
    workflowType: WorkflowType.Embed,
  }).catch(error => {
    console.error("Failed to fetch space data:", error);
    // Handle error appropriately
  });

  if (space) {
    redirect(`/embedded-portal/${space.id}`);
  } else {
    return <SetupSpace workflowType={WorkflowType.Embed} />;
  }

Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
export default async function Page() {
const session = await getServerSession();
invariant(session?.user, "User must be logged in");
const space = await SpaceService.getSpaceForWorkflow({
userId: session.user.id,
workflowType: WorkflowType.Embed,
});
if (space) {
redirect(`/embedded-portal/${space.id}`);
}
return <SetupSpace workflowType={WorkflowType.Embed} />;
export default async function Page() {
const session = await getServerSession();
invariant(session?.user, "User must be logged in");
const space = await SpaceService.getSpaceForWorkflow({
userId: session.user.id,
workflowType: WorkflowType.Embed,
}).catch(error => {
console.error("Failed to fetch space data:", error);
// Handle error appropriately
});
if (space) {
redirect(`/embedded-portal/${space.id}`);
} else {
return <SetupSpace workflowType={WorkflowType.Embed} />;
}

}
35 changes: 27 additions & 8 deletions app/(authenticated)/project-onboarding/setup-space.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,39 @@ import {
PROJECT_ONBOARDING_INITIAL_STEPS,
PROJECT_ONBOARDING_ITEM,
SAMPLE_DATA_FILENAME,
EMBEDDED_PORTAL_ITEM,
} from "@/lib/workflow-constants";
import { WorkflowType } from "@/lib/workflow-type";
import { useEffect, useState } from "react";

const STORAGE_KEY = `${process.env.NEXT_PUBLIC_APP_ID}-project-onboarding-downloaded`;
const EMBED_STORAGE_KEY = `${process.env.NEXT_PUBLIC_APP_ID}-embedded-portal-downloaded`;

export default function SetupSpace() {
export default function SetupSpace({
workflowType,
}: {
workflowType: WorkflowType;
}) {
const [steps, setSteps] = useState<Step[]>(PROJECT_ONBOARDING_INITIAL_STEPS);

let storageKey;
let item;
let spaceName;
if (workflowType === WorkflowType.Embed) {
storageKey = EMBED_STORAGE_KEY;
item = EMBEDDED_PORTAL_ITEM;
spaceName = "Embedded Portal";
} else {
storageKey = STORAGE_KEY;
item = PROJECT_ONBOARDING_ITEM;
spaceName = "Project Onboarding";
}
gaelyn marked this conversation as resolved.
Show resolved Hide resolved

useEffect(() => {
if (localStorage.getItem(STORAGE_KEY) === "true" && steps[0].status === "current") {
if (
localStorage.getItem(storageKey) === "true" &&
steps[0].status === "current"
) {
gaelyn marked this conversation as resolved.
Show resolved Hide resolved
Comment on lines +29 to +32
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider adding a dependency array to the useEffect hook to prevent unnecessary re-renders and ensure the effect runs only when specific values change.

  }, [steps, storageKey]);

Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
if (
localStorage.getItem(storageKey) === "true" &&
steps[0].status === "current"
) {
if (
localStorage.getItem(storageKey) === "true" &&
steps[0].status === "current"
) {

setSteps([
{ ...steps[0], status: "complete" },
{ ...steps[1], status: "current" },
Expand All @@ -28,13 +50,13 @@ export default function SetupSpace() {

return (
<div className="space-y-6">
<HeaderContent item={PROJECT_ONBOARDING_ITEM} steps={steps} />
<HeaderContent item={item} steps={steps} />

{steps[0].status === "current" && (
<DownloadSampleData
fileName={SAMPLE_DATA_FILENAME}
onClick={() => {
localStorage.setItem(STORAGE_KEY, "true");
localStorage.setItem(storageKey, "true");

setSteps([
{ ...steps[0], status: "complete" },
Expand All @@ -54,10 +76,7 @@ export default function SetupSpace() {
invite you to it. 👇
</p>

<CreateSpaceForm
workflowType={WorkflowType.ProjectOnboarding}
spaceName={"Project Onboarding"}
/>
<CreateSpaceForm workflowType={workflowType} spaceName={spaceName} />

<p className="text-xs block text-gray-400">
To download the sample data again,{" "}
Expand Down
2 changes: 2 additions & 0 deletions lib/workflow-constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,8 @@ export const WORKFLOW_ITEMS: {
export const PROJECT_ONBOARDING_ITEM =
WORKFLOW_ITEMS[WorkflowType.ProjectOnboarding];

export const EMBEDDED_PORTAL_ITEM = WORKFLOW_ITEMS[WorkflowType.Embed];

export const RESOURCE_ITEMS: NavItem[] = [
{
name: "Suppliers",
Expand Down
1 change: 1 addition & 0 deletions preflight.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ VARS = [
"NEXT_PUBLIC_FLATFILE_ENVIRONMENT_ID",
"NEXT_PUBLIC_APP_ID",
"LISTENER_AUTH_TOKEN",
"NEXT_PUBLIC_FLATFILE_NAMESPACE",
gaelyn marked this conversation as resolved.
Show resolved Hide resolved
];

function preflight() {
Expand Down
Loading