-
Notifications
You must be signed in to change notification settings - Fork 613
fix: add icon list #5163
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
Merged
Merged
fix: add icon list #5163
Changes from all commits
Commits
Show all changes
12 commits
Select commit
Hold shift + click to select a range
4e5d2d5
fix: add icon list
ogzhanolguncu c1ea45c
feat: add first step
ogzhanolguncu 2ba13d2
fix: overflow
ogzhanolguncu 1b34f22
feat: make structure more organized
ogzhanolguncu 16b05e9
feat: add callout to top
ogzhanolguncu 4712870
feat: add project creation
ogzhanolguncu 69ad801
feat: add repo selection
ogzhanolguncu 5d7df92
chore: tidy up github
ogzhanolguncu adbd994
chore: fmt
ogzhanolguncu fb3225e
fix: comments
ogzhanolguncu e6a0cfa
fix: build issue
ogzhanolguncu 8bed7eb
fix: missing path
ogzhanolguncu File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
29 changes: 29 additions & 0 deletions
29
web/apps/dashboard/app/(app)/[workspaceSlug]/projects/(onboarding)/index.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| "use client"; | ||
| import { StepWizard } from "@unkey/ui"; | ||
| import { useState } from "react"; | ||
| import { OnboardingHeader } from "./onboarding-header"; | ||
| import { ConnectGithubStep } from "./steps/connect-github"; | ||
| import { CreateProjectStep } from "./steps/create-project"; | ||
| import { SelectRepo } from "./steps/select-repo"; | ||
|
|
||
| export const Onboarding = () => { | ||
| const [projectId, setProjectId] = useState<string | null>(null); | ||
|
|
||
| return ( | ||
| <div className="flex flex-col items-center justify-center h-screen relative"> | ||
| <StepWizard.Root> | ||
| <OnboardingHeader projectId={projectId} /> | ||
| <StepWizard.Step id="create-project" label="Create project"> | ||
| <CreateProjectStep onProjectCreated={setProjectId} /> | ||
| </StepWizard.Step> | ||
| <StepWizard.Step id="connect-github" label="Connect GitHub"> | ||
| <ConnectGithubStep projectId={projectId} /> | ||
| </StepWizard.Step> | ||
| <StepWizard.Step id="select-repo" label="Connect GitHub"> | ||
| {/* // Clean this up later. ProjectId cannot be null after the first step */} | ||
| <SelectRepo projectId={projectId ?? undefined} /> | ||
| </StepWizard.Step> | ||
| </StepWizard.Root> | ||
| </div> | ||
| ); | ||
| }; |
147 changes: 147 additions & 0 deletions
147
web/apps/dashboard/app/(app)/[workspaceSlug]/projects/(onboarding)/onboarding-header.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,147 @@ | ||
| "use client"; | ||
| import { trpc } from "@/lib/trpc/client"; | ||
| import { Check, CloudUp, Harddrive, HeartPulse, Location2, Nodes2, XMark } from "@unkey/icons"; | ||
| import { useStepWizard } from "@unkey/ui"; | ||
| import { cn } from "@unkey/ui/src/lib/utils"; | ||
| import { useState } from "react"; | ||
|
|
||
| type IconBoxProps = { | ||
| children?: React.ReactNode; | ||
| large?: boolean; | ||
| className?: string; | ||
| }; | ||
|
|
||
| const IconBox = ({ children, large, className }: IconBoxProps) => ( | ||
| <div | ||
| className={cn( | ||
| "shrink-0 flex items-center justify-center rounded-[10px] bg-transparent ring-1 ring-grayA-4 shadow-[0_2px_8px_-2px_rgba(0,0,0,0.12),0_0_0_0.75px_rgba(0,0,0,0.08)]", | ||
| large ? "size-16" : "size-9", | ||
| className, | ||
| )} | ||
| > | ||
| {children} | ||
| </div> | ||
| ); | ||
|
|
||
| const iconItems: { icon: React.ReactNode; large?: boolean; opacity: string }[] = [ | ||
| { icon: null, opacity: "opacity-60" }, | ||
| { icon: <Harddrive className="size-[18px]" iconSize="md-medium" />, opacity: "opacity-75" }, | ||
| { icon: <Location2 className="size-[18px]" iconSize="md-medium" />, opacity: "opacity-80" }, | ||
| { icon: <CloudUp className="size-9" iconSize="md-thin" />, large: true, opacity: "opacity-90" }, | ||
| { icon: <HeartPulse className="size-[18px]" iconSize="md-medium" />, opacity: "opacity-80" }, | ||
| { icon: <Nodes2 className="size-[18px]" iconSize="md-medium" />, opacity: "opacity-75" }, | ||
| { icon: null, opacity: "opacity-60" }, | ||
| ]; | ||
|
|
||
| const IconRow = () => ( | ||
| <div | ||
| className="p-2" | ||
| style={{ | ||
| maskImage: "linear-gradient(to right, transparent, black 15%, black 85%, transparent)", | ||
| WebkitMaskImage: "linear-gradient(to right, transparent, black 15%, black 85%, transparent)", | ||
| }} | ||
| > | ||
| <div className="flex gap-6 items-center justify-center text-gray-12"> | ||
| {iconItems.map((item, i) => ( | ||
| // biome-ignore lint/suspicious/noArrayIndexKey: its okay | ||
| <IconBox key={i} large={item.large} className={item.opacity}> | ||
| {item.icon} | ||
| </IconBox> | ||
| ))} | ||
| </div> | ||
| </div> | ||
| ); | ||
|
|
||
| type StepConfig = { | ||
| title: string; | ||
| subtitle: React.ReactNode; | ||
| showIconRow: boolean; | ||
| }; | ||
|
|
||
| const stepConfigs: Record<string, StepConfig> = { | ||
| "create-project": { | ||
| title: "Deploy your first project", | ||
| subtitle: ( | ||
| <> | ||
| Connect a GitHub repo and get a live URL in minutes. | ||
| <br /> | ||
| Unkey handles builds, infra, scaling, and routing. | ||
| </> | ||
| ), | ||
| showIconRow: true, | ||
| }, | ||
| "connect-github": { | ||
| title: "Deploy your first project", | ||
| subtitle: ( | ||
| <> | ||
| Connect a GitHub repo and get a live URL in minutes. | ||
| <br /> | ||
| Unkey handles builds, infra, scaling, and routing. | ||
| </> | ||
| ), | ||
| showIconRow: true, | ||
| }, | ||
| "select-repo": { | ||
| title: "Select a repository", | ||
| subtitle: ( | ||
| <> | ||
| Choose a repository and a branch containing your project. | ||
| <br /> | ||
| We’ll automatically detect Dockerfiles. | ||
| </> | ||
| ), | ||
| showIconRow: false, | ||
| }, | ||
| }; | ||
|
|
||
| type OnboardingHeaderProps = { | ||
| projectId: string | null; | ||
| }; | ||
|
|
||
| export const OnboardingHeader = ({ projectId }: OnboardingHeaderProps) => { | ||
| const { activeStepId } = useStepWizard(); | ||
| const [isDismissed, setIsDismissed] = useState(false); | ||
| const config = stepConfigs[activeStepId]; | ||
|
|
||
| const isGithubStep = activeStepId === "connect-github"; | ||
| const { data } = trpc.github.getInstallations.useQuery( | ||
| { projectId: projectId ?? "" }, | ||
| { enabled: isGithubStep && Boolean(projectId), staleTime: 0 }, | ||
| ); | ||
| const hasInstallations = (data?.installations?.length ?? 0) > 0; | ||
| const showBanner = isGithubStep && hasInstallations && !isDismissed; | ||
|
|
||
| if (!config) { | ||
| return null; | ||
| } | ||
|
|
||
| return ( | ||
| <> | ||
| {showBanner && ( | ||
| <div className="absolute top-2 left-2 right-2 rounded-[10px] p-3 gap-2.5 flex items-center shadow-[inset_0_0_0_0.75px_rgba(0,0,0,0.10)] bg-gradient-to-r from-successA-4 via-successA-1 to-success-1"> | ||
| <Check iconSize="sm-regular" /> | ||
| <div className="flex items-center gap-1"> | ||
| <span className="font-medium text-[13px] text-success-12"> | ||
| GitHub connected successfully. | ||
| </span> | ||
| <span className="text-[13px] text-success-12"> | ||
| You can now select a repository to deploy | ||
| </span> | ||
| </div> | ||
| <button type="button" onClick={() => setIsDismissed(true)} className="ml-auto"> | ||
| <XMark iconSize="sm-regular" /> | ||
| </button> | ||
| </div> | ||
| )} | ||
| <div className="flex flex-col items-center"> | ||
| {config.showIconRow && <IconRow />} | ||
| <div className="mb-5" /> | ||
| <div className="flex flex-col items-center justify-center gap-2"> | ||
| <div className="font-semibold text-lg text-gray-12">{config.title}</div> | ||
| <div className="text-[13px] text-gray-11 text-center">{config.subtitle}</div> | ||
| </div> | ||
| <div className="mb-6" /> | ||
| </div> | ||
| </> | ||
| ); | ||
| }; |
46 changes: 46 additions & 0 deletions
46
web/apps/dashboard/app/(app)/[workspaceSlug]/projects/(onboarding)/steps/connect-github.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,46 @@ | ||
| "use client"; | ||
| import { Github, Layers3 } from "@unkey/icons"; | ||
| import { Button } from "@unkey/ui"; | ||
| import { OnboardingLinks } from "./onboarding-links"; | ||
|
|
||
| type ConnectGithubStepProps = { | ||
| projectId: string | null; | ||
| }; | ||
|
|
||
| export const ConnectGithubStep = ({ projectId }: ConnectGithubStepProps) => { | ||
| const installUrl = `https://github.com/apps/${process.env.NEXT_PUBLIC_GITHUB_APP_NAME}/installations/new?state=${encodeURIComponent(JSON.stringify({ projectId }))}`; | ||
|
|
||
| return ( | ||
| <div className="flex flex-col items-center"> | ||
| <div className="border border-grayA-5 rounded-[14px] flex justify-center items-center gap-4 py-[18px] px-4 min-w-[600px]"> | ||
| <div className="size-8 rounded-[10px] bg-gray-12 grid place-items-center"> | ||
| <Layers3 className="size-[18px] text-gray-1" iconSize="md-medium" /> | ||
| </div> | ||
| <div className="flex flex-col gap-3"> | ||
| <span className="font-medium text-gray-12 text-[13px] leading-[9px]">Import project</span> | ||
| <span className="text-gray-10 text-[13px] leading-[9px]"> | ||
| Add a repo from your GitHub account | ||
| </span> | ||
| </div> | ||
| <a | ||
| href={installUrl} | ||
| target="_blank" | ||
| rel="noopener noreferrer" | ||
| className={projectId ? "" : "pointer-events-none opacity-50"} | ||
| aria-disabled={!projectId} | ||
| > | ||
| <Button | ||
| variant="outline" | ||
| className="ml-20 rounded-lg border-grayA-4 hover:bg-grayA-2 shadow-sm hover:shadow-md transition-all" | ||
| disabled={!projectId} | ||
| > | ||
| <Github className="!size-[18px] text-gray-12 shrink-0" /> | ||
| <span className="text-[13px] text-gray-12 font-medium">Import from GitHub</span> | ||
| </Button> | ||
| </a> | ||
| </div> | ||
| <div className="mb-7" /> | ||
| <OnboardingLinks /> | ||
| </div> | ||
| ); | ||
| }; |
125 changes: 125 additions & 0 deletions
125
web/apps/dashboard/app/(app)/[workspaceSlug]/projects/(onboarding)/steps/create-project.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,125 @@ | ||
| "use client"; | ||
| import { collection } from "@/lib/collections"; | ||
| import { | ||
| type CreateProjectRequestSchema, | ||
| createProjectRequestSchema, | ||
| } from "@/lib/collections/deploy/projects"; | ||
| import { zodResolver } from "@hookform/resolvers/zod"; | ||
| import { DuplicateKeyError } from "@tanstack/react-db"; | ||
| import { Button, FormInput, useStepWizard } from "@unkey/ui"; | ||
| import { useForm } from "react-hook-form"; | ||
| import { OnboardingLinks } from "./onboarding-links"; | ||
|
|
||
| type CreateProjectStepProps = { | ||
| onProjectCreated: (id: string) => void; | ||
| }; | ||
|
|
||
| export const CreateProjectStep = ({ onProjectCreated }: CreateProjectStepProps) => { | ||
| const { next } = useStepWizard(); | ||
|
|
||
| const { | ||
| register, | ||
| handleSubmit, | ||
| setValue, | ||
| setError, | ||
| formState: { errors, isSubmitting, isValid }, | ||
| } = useForm<CreateProjectRequestSchema>({ | ||
| resolver: zodResolver(createProjectRequestSchema), | ||
| defaultValues: { | ||
| name: "", | ||
| slug: "", | ||
| }, | ||
| mode: "onChange", | ||
| }); | ||
|
|
||
| const onSubmitForm = async (values: CreateProjectRequestSchema) => { | ||
| try { | ||
| const tx = collection.projects.insert({ | ||
| name: values.name, | ||
| slug: values.slug, | ||
| repositoryFullName: null, | ||
| liveDeploymentId: null, | ||
| isRolledBack: false, | ||
| id: "will-be-replace-by-server", | ||
| latestDeploymentId: null, | ||
| author: "will-be-replace-by-server", | ||
| authorAvatar: "will-be-replace-by-server", | ||
| branch: "will-be-replace-by-server", | ||
| commitTimestamp: Date.now(), | ||
| commitTitle: "will-be-replace-by-server", | ||
| domain: "will-be-replace-by-server", | ||
| regions: [], | ||
| }); | ||
| await tx.isPersisted.promise; | ||
| // await collection.projects.utils.refetch(); | ||
| const created = collection.projects.toArray.find((p) => p.slug === values.slug); | ||
| if (created) { | ||
| onProjectCreated(created.id); | ||
| } | ||
| next(); | ||
| } catch (error) { | ||
| if (error instanceof DuplicateKeyError) { | ||
| setError("slug", { | ||
| type: "custom", | ||
| message: "Project with this slug already exists", | ||
| }); | ||
| } else { | ||
| console.error("Form submission error:", error); | ||
| } | ||
| } | ||
| }; | ||
|
|
||
| const handleNameChange = (e: React.ChangeEvent<HTMLInputElement>) => { | ||
| const name = e.target.value; | ||
| const slug = name | ||
| .toLowerCase() | ||
| .replace(/[^a-z0-9\s-]/g, "") | ||
| .replace(/\s+/g, "-") | ||
| .replace(/-+/g, "-") | ||
| .replace(/^-|-$/g, ""); | ||
| setValue("slug", slug); | ||
| }; | ||
|
|
||
| return ( | ||
| <div className="w-full justify-center items-center flex flex-col"> | ||
| <div className="flex flex-col items-center border border-grayA-5 rounded-[14px] justify-center gap-4 py-[18px] px-4 min-w-[600px]"> | ||
| <form onSubmit={handleSubmit(onSubmitForm)} className="flex flex-col gap-4 w-full"> | ||
| <FormInput | ||
| required | ||
| label="Project Name" | ||
| className="[&_input:first-of-type]:h-[36px]" | ||
| description="A descriptive name for your project." | ||
| error={errors.name?.message} | ||
| {...register("name", { | ||
| onChange: handleNameChange, | ||
| })} | ||
| placeholder="My Awesome Project" | ||
| /> | ||
|
|
||
| <FormInput | ||
| required | ||
| label="Slug" | ||
| className="[&_input:first-of-type]:h-[36px]" | ||
| description="URL-friendly identifier for your project (auto-generated from name)." | ||
| error={errors.slug?.message} | ||
| {...register("slug")} | ||
| placeholder="my-awesome-project" | ||
| /> | ||
|
|
||
| <Button | ||
| type="submit" | ||
| variant="primary" | ||
| size="xlg" | ||
| disabled={isSubmitting || !isValid} | ||
| loading={isSubmitting} | ||
| className="w-full rounded-lg mt-2" | ||
| > | ||
| Create Project | ||
| </Button> | ||
| </form> | ||
| </div> | ||
| <div className="mb-7" /> | ||
| <OnboardingLinks /> | ||
| </div> | ||
| ); | ||
| }; |
41 changes: 41 additions & 0 deletions
41
...apps/dashboard/app/(app)/[workspaceSlug]/projects/(onboarding)/steps/onboarding-links.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| import { BookBookmark, Discord } from "@unkey/icons"; | ||
| import { Button } from "@unkey/ui"; | ||
|
|
||
| export const OnboardingLinks = () => ( | ||
| <div className="flex gap-3 items-center"> | ||
| <Button | ||
| variant="outline" | ||
| className="text-gray-12 text-[13px] font-medium border border-grayA-4 rounded-full px-3 py-1.5 transition-all " | ||
| > | ||
| <a | ||
| href="https://www.unkey.com/docs/introduction" | ||
| target="_blank" | ||
| rel="noopener noreferrer" | ||
| className="flex items-center w-full gap-2" | ||
| > | ||
| <BookBookmark className="text-gray-12 shrink-0 size-[18px]" iconSize="sm-regular" /> | ||
| View documentation | ||
| </a> | ||
| </Button> | ||
| <Button | ||
| variant="outline" | ||
| className="text-gray-12 text-[13px] font-medium border border-grayA-4 rounded-full px-3 py-1.5 transition-all " | ||
| > | ||
| <a | ||
| href="https://unkey.com/discord" | ||
| target="_blank" | ||
| rel="noopener noreferrer" | ||
| className="flex items-center w-full gap-2" | ||
| > | ||
| <div className="size-[18px] overflow-hidden flex items-center justify-center"> | ||
| <Discord | ||
| className="text-feature-11 shrink-0" | ||
| style={{ width: 18, height: 18 }} | ||
| iconSize="sm-regular" | ||
| /> | ||
| </div> | ||
| Join community | ||
| </a> | ||
| </Button> | ||
| </div> | ||
| ); | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.