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

Get Started checklist #232

Merged
merged 6 commits into from
Jul 11, 2022
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
10 changes: 10 additions & 0 deletions packages/services/api/src/modules/organization/module.graphql.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ export default gql`
me: Member!
members: MemberConnection!
inviteCode: String!
getStarted: OrganizationGetStarted!
}

type OrganizationConnection {
Expand All @@ -114,4 +115,13 @@ export default gql`
selector: OrganizationSelector!
organization: Organization!
}

type OrganizationGetStarted {
creatingProject: Boolean!
publishingSchema: Boolean!
checkingSchema: Boolean!
invitingMembers: Boolean!
reportingOperations: Boolean!
enablingUsageBasedBreakingChanges: Boolean!
}
`;
3 changes: 3 additions & 0 deletions packages/services/api/src/modules/organization/resolvers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,9 @@ export const resolvers: OrganizationModule.Resolvers = {
members(organization, _, { injector }) {
return injector.get(OrganizationManager).getOrganizationMembers({ organization: organization.id });
},
getStarted(organization) {
return organization.getStarted;
},
kamilkisiela marked this conversation as resolved.
Show resolved Hide resolved
},
OrganizationInvitationError: {
__isTypeOf(obj) {
Expand Down
8 changes: 8 additions & 0 deletions packages/services/api/src/shared/entities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,14 @@ export interface Organization {
operations: number;
schemaPush: number;
};
getStarted: {
creatingProject: boolean;
publishingSchema: boolean;
checkingSchema: boolean;
invitingMembers: boolean;
reportingOperations: boolean;
enablingUsageBasedBreakingChanges: boolean;
};
}

export interface OrganizationBilling {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
-- Tracks feature discovery progress

ALTER table public.organizations
ADD COLUMN get_started_creating_project BOOLEAN NOT NULL DEFAULT FALSE,
ADD COLUMN get_started_publishing_schema BOOLEAN NOT NULL DEFAULT FALSE,
ADD COLUMN get_started_checking_schema BOOLEAN NOT NULL DEFAULT FALSE,
ADD COLUMN get_started_inviting_members BOOLEAN NOT NULL DEFAULT FALSE,
ADD COLUMN get_started_reporting_operations BOOLEAN NOT NULL DEFAULT FALSE,
ADD COLUMN get_started_usage_breaking BOOLEAN NOT NULL DEFAULT FALSE;
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
ALTER TABLE public.commits
DROP COLUMN get_started_creating_project,
DROP COLUMN get_started_publishing_schema,
DROP COLUMN get_started_checking_schema,
DROP COLUMN get_started_inviting_members,
DROP COLUMN get_started_reporting_operations,
DROP COLUMN get_started_usage_breaking;
kamilkisiela marked this conversation as resolved.
Show resolved Hide resolved
6 changes: 6 additions & 0 deletions packages/services/storage/src/db/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,12 @@ export interface organization_member {
export interface organizations {
clean_id: string;
created_at: Date;
get_started_checking_schema: boolean;
get_started_creating_project: boolean;
get_started_inviting_members: boolean;
get_started_publishing_schema: boolean;
get_started_reporting_operations: boolean;
get_started_usage_breaking: boolean;
github_app_installation_id: string | null;
id: string;
invite_code: string;
Expand Down
8 changes: 8 additions & 0 deletions packages/services/storage/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,14 @@ export async function createStorage(connection: string): Promise<Storage> {
},
billingPlan: organization.plan_name,
type: (organization.type === 'PERSONAL' ? 'PERSONAL' : 'REGULAR') as OrganizationType,
getStarted: {
creatingProject: organization.get_started_creating_project,
publishingSchema: organization.get_started_publishing_schema,
checkingSchema: organization.get_started_checking_schema,
invitingMembers: organization.get_started_inviting_members,
reportingOperations: organization.get_started_reporting_operations,
enablingUsageBasedBreakingChanges: organization.get_started_usage_breaking,
},
};
}

Expand Down
153 changes: 153 additions & 0 deletions packages/web/app/src/components/get-started/wizard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
import React from 'react';
import { VscIssues, VscError } from 'react-icons/vsc';
import {
useDisclosure,
Drawer,
DrawerBody,
DrawerHeader,
DrawerOverlay,
DrawerContent,
DrawerCloseButton,
} from '@chakra-ui/react';
import clsx from 'clsx';
import { gql, DocumentType } from 'urql';

const GetStartedWizard_GetStartedProgress = gql(/* GraphQL */ `
fragment GetStartedWizard_GetStartedProgress on OrganizationGetStarted {
creatingProject
publishingSchema
checkingSchema
invitingMembers
reportingOperations
enablingUsageBasedBreakingChanges
}
`);

export function GetStartedProgress({ tasks }: { tasks: DocumentType<typeof GetStartedWizard_GetStartedProgress> }) {
const { isOpen, onOpen, onClose } = useDisclosure();
const triggerRef = React.useRef<HTMLButtonElement>(null);

if (!tasks) {
return null;
}

const values = Object.values(tasks);
const total = values.length;
const completed = values.filter(t => t === true).length;
const remaining = total - completed;

if (remaining === 0) {
return null;
}

return (
<>
<button onClick={onOpen} className="cursor-pointer rounded px-4 py-2 text-left hover:opacity-80" ref={triggerRef}>
<div className="font-medium">Get Started</div>
<div className="text-xs text-gray-500">
{remaining} remaining task{remaining > 1 ? 's' : ''}
</div>
<div>
<div
className="relative mt-1 w-full overflow-hidden rounded bg-gray-800"
style={{
height: 5,
}}
>
<div
className="bg-orange-500 h-full"
style={{
width: `${(completed / total) * 100}%`,
}}
/>
</div>
</div>
</button>
<GetStartedWizard isOpen={isOpen} onClose={onClose} triggerRef={triggerRef} tasks={tasks} />
</>
);
}

function GetStartedWizard({
isOpen,
onClose,
triggerRef,
tasks,
}: {
isOpen: boolean;
onClose(): void;
triggerRef: React.RefObject<HTMLButtonElement>;
tasks: DocumentType<typeof GetStartedWizard_GetStartedProgress>;
}) {
return (
<Drawer isOpen={isOpen} placement="right" onClose={onClose} finalFocusRef={triggerRef} size="md">
<DrawerOverlay />
<DrawerContent bgColor={'gray.800'}>
<DrawerCloseButton />
<DrawerHeader>Get Started</DrawerHeader>
<DrawerBody>
<p>Complete these steps to experience the full power of GraphQL Hive</p>
<div className="mt-4 flex flex-col divide-y-2 divide-gray-900">
<Task link={`${process.env.NEXT_PUBLIC_DOCS_LINK}/get-started/projects`} completed={tasks.creatingProject}>
Create a project
</Task>
<Task
link={`${process.env.NEXT_PUBLIC_DOCS_LINK}/features/publish-schema`}
completed={tasks.publishingSchema}
>
Publish a schema
</Task>
<Task
link={`${process.env.NEXT_PUBLIC_DOCS_LINK}/features/checking-schema`}
completed={tasks.checkingSchema}
>
Check a schema
</Task>
<Task
link={`${process.env.NEXT_PUBLIC_DOCS_LINK}/get-started/organizations#members`}
completed={tasks.invitingMembers}
>
Invite members
</Task>
<Task
link={`${process.env.NEXT_PUBLIC_DOCS_LINK}/features/monitoring`}
completed={tasks.reportingOperations}
>
Report operations
</Task>
<Task
link={`${process.env.NEXT_PUBLIC_DOCS_LINK}/features/checking-schema#with-usage-enabled`}
completed={tasks.enablingUsageBasedBreakingChanges}
>
Enable usage-based breaking changes
</Task>
</div>
</DrawerBody>
</DrawerContent>
</Drawer>
);
}

function Task({
completed,
children,
link,
}: React.PropsWithChildren<{
completed: boolean;
link: string;
}>) {
return (
<a
Fixed Show fixed Hide fixed
href={link}
target="_blank"
className={clsx('flex flex-row items-center gap-4 p-3 text-left', completed ? 'opacity-50' : 'hover:opacity-80')}
>
{completed ? (
<VscIssues className="h-[20px] w-[20px] text-green-400" />
) : (
<VscError className="h-[20px] w-[20px] text-red-400" />
)}
{children}
</a>
);
}