+ const renderLabels = useMemo(() => {
+ if (!label && !description) return null;
+ return (
+
- ),
- [theme, label, description],
- );
+ );
+ }, [theme, label, description]);
const renderLeftIcon = useMemo(() => {
return (
@@ -320,8 +302,9 @@ export const Select = ({
);
}, [cloneIcon, theme, rightIcon]);
- const renderHelperText = useMemo(
- () => (
+ const renderHelperText = useMemo(() => {
+ if (!helperText) return null;
+ return (
{error &&
cloneIcon(
, {
@@ -329,13 +312,13 @@ export const Select = ({
})}
{helperText}
- ),
- [cloneIcon, error, theme, helperText],
- );
+ );
+ }, [cloneIcon, error, theme, helperText]);
const isMultipleHasValue = multiple && selectedItems.length > 0;
const isMultipleHasValueButNotSearchable =
multiple && !searchable && selectedItems.length > 0;
+
const displayPlaceholder = useMemo(() => {
if (hideValues && isMultipleHasValue) {
return `${selectedItems.length} selected`;
@@ -391,6 +374,8 @@ export const Select = ({
'w-6': isMultipleHasValueButNotSearchable && !hideValues,
// Add margin to the X icon
'ml-6': isMultipleHasValueButNotSearchable && clearable,
+ // Add padding if there's a left icon
+ 'pl-7': leftIcon,
},
)}
/>
diff --git a/packages/frontend/src/components/shared/Steps/Step/Step.theme.ts b/packages/frontend/src/components/shared/Steps/Step/Step.theme.ts
new file mode 100644
index 00000000..8e2b7a53
--- /dev/null
+++ b/packages/frontend/src/components/shared/Steps/Step/Step.theme.ts
@@ -0,0 +1,52 @@
+import { VariantProps, tv } from 'tailwind-variants';
+
+export const stepTheme = tv({
+ slots: {
+ wrapper: ['relative', 'px-1.5', 'py-1.5', 'flex', 'gap-2', 'items-center'],
+ step: [
+ 'bg-base-bg-emphasized',
+ 'rounded-full',
+ 'w-7',
+ 'h-7',
+ 'flex',
+ 'items-center',
+ 'justify-center',
+ 'text-elements-mid-em',
+ 'shadow-button',
+ 'shrink-0',
+ ],
+ label: [
+ 'text-sm',
+ 'font-sans',
+ 'text-elements-mid-em',
+ 'whitespace-nowrap',
+ ],
+ connector: [],
+ },
+ variants: {
+ orientation: {
+ vertical: {
+ connector: ['bg-border-interactive-hovered', 'w-px', 'h-3', 'ml-5'],
+ },
+ horizontal: {
+ connector: ['text-border-interactive-hovered', 'h-3', 'w-3'],
+ },
+ },
+ active: {
+ true: {
+ step: ['bg-controls-secondary-hovered', 'text-elements-on-secondary'],
+ label: ['text-elements-high-em'],
+ },
+ },
+ completed: {
+ true: {
+ step: ['text-controls-primary'],
+ },
+ },
+ },
+ defaultVariants: {
+ orientation: 'vertical',
+ },
+});
+
+export type StepTheme = VariantProps
;
diff --git a/packages/frontend/src/components/shared/Steps/Step/Step.tsx b/packages/frontend/src/components/shared/Steps/Step/Step.tsx
new file mode 100644
index 00000000..9d731139
--- /dev/null
+++ b/packages/frontend/src/components/shared/Steps/Step/Step.tsx
@@ -0,0 +1,67 @@
+import React, { useCallback, ComponentPropsWithoutRef } from 'react';
+import { stepTheme, StepTheme } from './Step.theme';
+import {
+ CheckRoundFilledIcon,
+ ChevronRight,
+} from 'components/shared/CustomIcon';
+
+export interface StepProps extends ComponentPropsWithoutRef<'li'>, StepTheme {
+ /**
+ * The label for the step
+ */
+ label: string;
+ /**
+ * The index of the step
+ */
+ index: number;
+ /**
+ * The total number of steps
+ */
+ currentIndex: number;
+}
+
+export const Step = ({
+ label,
+ index,
+ currentIndex,
+ orientation,
+ ...props
+}: StepProps) => {
+ const theme = stepTheme();
+
+ const active = currentIndex === index;
+ const completed = currentIndex > index;
+
+ const renderConnector = useCallback(
+ (index: number) => {
+ if (index === 1) {
+ return null;
+ }
+
+ return (
+
+ {orientation === 'horizontal' && }
+
+ );
+ },
+ [orientation, theme],
+ );
+
+ return (
+ <>
+ {renderConnector(index)}
+
+ {
+
+ {completed ? (
+
+ ) : (
+ index
+ )}
+
+ }
+ {label}
+
+ >
+ );
+};
diff --git a/packages/frontend/src/components/shared/Steps/Step/index.ts b/packages/frontend/src/components/shared/Steps/Step/index.ts
new file mode 100644
index 00000000..84d83fcb
--- /dev/null
+++ b/packages/frontend/src/components/shared/Steps/Step/index.ts
@@ -0,0 +1,2 @@
+export * from './Step';
+export * from './Step.theme';
diff --git a/packages/frontend/src/components/shared/Steps/Steps.theme.ts b/packages/frontend/src/components/shared/Steps/Steps.theme.ts
new file mode 100644
index 00000000..4c3b2acb
--- /dev/null
+++ b/packages/frontend/src/components/shared/Steps/Steps.theme.ts
@@ -0,0 +1,18 @@
+import { VariantProps, tv } from 'tailwind-variants';
+
+export const stepsTheme = tv({
+ slots: {
+ root: [],
+ },
+ variants: {
+ orientation: {
+ vertical: { root: ['flex', 'flex-col'] },
+ horizontal: { root: ['flex', 'items-center', 'gap-1'] },
+ },
+ },
+ defaultVariants: {
+ orientation: 'vertical',
+ },
+});
+
+export type StepsTheme = VariantProps;
diff --git a/packages/frontend/src/components/shared/Steps/Steps.tsx b/packages/frontend/src/components/shared/Steps/Steps.tsx
new file mode 100644
index 00000000..cac61ff9
--- /dev/null
+++ b/packages/frontend/src/components/shared/Steps/Steps.tsx
@@ -0,0 +1,42 @@
+import React, { Fragment, ComponentPropsWithoutRef } from 'react';
+import { stepsTheme, StepsTheme } from './Steps.theme';
+import { Step, StepProps, StepTheme } from './Step';
+
+interface StepsProps
+ extends ComponentPropsWithoutRef<'ul'>,
+ StepsTheme,
+ Pick {
+ /**
+ * The index of the current step
+ */
+ currentIndex: number;
+ /**
+ * The steps to render
+ */
+ steps: Pick[];
+}
+
+export const Steps = ({
+ currentIndex,
+ steps = [],
+ className,
+ orientation,
+ ...props
+}: StepsProps) => {
+ const theme = stepsTheme();
+
+ return (
+
+ {steps.map((step, i) => (
+
+
+
+ ))}
+
+ );
+};
diff --git a/packages/frontend/src/components/shared/Steps/index.ts b/packages/frontend/src/components/shared/Steps/index.ts
new file mode 100644
index 00000000..27d56a3b
--- /dev/null
+++ b/packages/frontend/src/components/shared/Steps/index.ts
@@ -0,0 +1,2 @@
+export * from './Steps';
+export * from './Steps.theme';
diff --git a/packages/frontend/src/components/shared/Tabs/Tabs.theme.ts b/packages/frontend/src/components/shared/Tabs/Tabs.theme.ts
index 81a32589..783d242b 100644
--- a/packages/frontend/src/components/shared/Tabs/Tabs.theme.ts
+++ b/packages/frontend/src/components/shared/Tabs/Tabs.theme.ts
@@ -71,6 +71,7 @@ export const tabsTheme = tv({
'gap-5',
'border-b',
'border-transparent',
+ 'overflow-scroll',
// Horizontal – default
'data-[orientation=horizontal]:border-border-interactive/10',
// Vertical
diff --git a/packages/frontend/src/pages/components/index.tsx b/packages/frontend/src/pages/components/index.tsx
index aab2d652..feee4595 100644
--- a/packages/frontend/src/pages/components/index.tsx
+++ b/packages/frontend/src/pages/components/index.tsx
@@ -26,6 +26,7 @@ import {
import { renderInputs } from './renders/input';
import { RADIO_OPTIONS } from './renders/radio';
import { SEGMENTED_CONTROLS_OPTIONS } from './renders/segmentedControls';
+import { renderHorizontalSteps, renderVerticalSteps } from './renders/steps';
import {
renderTabWithBadges,
renderTabs,
@@ -56,6 +57,19 @@ const Page: React.FC = () => {
+ {/* Steps */}
+
+
+
Steps
+
+ {renderVerticalSteps()}
+ {renderHorizontalSteps()}
+
+
+
+
+
+
{/* Tag */}
diff --git a/packages/frontend/src/pages/components/renders/steps.tsx b/packages/frontend/src/pages/components/renders/steps.tsx
new file mode 100644
index 00000000..2d3f3da5
--- /dev/null
+++ b/packages/frontend/src/pages/components/renders/steps.tsx
@@ -0,0 +1,41 @@
+import React from 'react';
+import { Steps } from 'components/shared/Steps';
+
+export const renderVerticalSteps = () => {
+ return (
+
+ );
+};
+
+export const renderHorizontalSteps = () => {
+ return (
+
+ );
+};
diff --git a/packages/frontend/src/pages/org-slug/projects/Id.tsx b/packages/frontend/src/pages/org-slug/projects/Id.tsx
index 8b164852..9db6dee1 100644
--- a/packages/frontend/src/pages/org-slug/projects/Id.tsx
+++ b/packages/frontend/src/pages/org-slug/projects/Id.tsx
@@ -7,6 +7,7 @@ import {
useParams,
} from 'react-router-dom';
import { Project as ProjectType } from 'gql-client';
+import { useMediaQuery } from 'usehooks-ts';
import { useGQLClient } from '../../../context/GQLClientContext';
import { useOctokit } from '../../../context/OctokitContext';
@@ -23,6 +24,9 @@ const Id = () => {
const client = useGQLClient();
const location = useLocation();
+ const isDesktopView = useMediaQuery('(min-width: 768px)'); // md:
+ const buttonSize = isDesktopView ? {} : { size: 'sm' as const };
+
const [project, setProject] = useState
(null);
const [repoUrl, setRepoUrl] = useState('');
@@ -65,25 +69,32 @@ const Id = () => {
{project ? (
<>
-
+
}
onClick={() => navigate(-1)}
/>
-
+
{project?.name}
-
@@ -96,9 +107,6 @@ const Id = () => {
Deployments
-
- Database
-
Integrations
diff --git a/packages/frontend/src/pages/org-slug/projects/create/Template.tsx b/packages/frontend/src/pages/org-slug/projects/create/Template.tsx
index d819a6f1..3d61792d 100644
--- a/packages/frontend/src/pages/org-slug/projects/create/Template.tsx
+++ b/packages/frontend/src/pages/org-slug/projects/create/Template.tsx
@@ -6,10 +6,14 @@ import {
useSearchParams,
} from 'react-router-dom';
-import { Avatar } from '@material-tailwind/react';
-
-import Stepper from '../../../../components/Stepper';
import templates from '../../../../assets/templates';
+import {
+ LinkChainIcon,
+ TemplateIcon,
+ TemplateIconType,
+} from 'components/shared/CustomIcon';
+import { Heading } from 'components/shared/Heading';
+import { Steps } from 'components/shared/Steps';
// TODO: Set dynamic route for template and load details from DB
const CreateWithTemplate = () => {
@@ -44,25 +48,30 @@ const CreateWithTemplate = () => {
return (
-
-
-
{template?.name}
+
-
+
diff --git a/packages/frontend/src/pages/org-slug/projects/create/index.tsx b/packages/frontend/src/pages/org-slug/projects/create/index.tsx
index 1c86ee3b..49eeed96 100644
--- a/packages/frontend/src/pages/org-slug/projects/create/index.tsx
+++ b/packages/frontend/src/pages/org-slug/projects/create/index.tsx
@@ -1,7 +1,7 @@
import React from 'react';
import templates from 'assets/templates';
-import RepositoryList from 'components/projects/create/RepositoryList';
+import { RepositoryList } from 'components/projects/create/RepositoryList';
import ConnectAccount from 'components/projects/create/ConnectAccount';
import { useOctokit } from 'context/OctokitContext';
import { Heading } from 'components/shared/Heading';
@@ -13,8 +13,8 @@ const NewProject = () => {
return isAuth ? (
<>
-
- Start with template
+
+ Start with a template
{templates.map((template) => {
@@ -28,7 +28,7 @@ const NewProject = () => {
})}
-
+
Import a repository
diff --git a/packages/frontend/src/pages/org-slug/projects/create/template/index.tsx b/packages/frontend/src/pages/org-slug/projects/create/template/index.tsx
index 9ff50c84..9fc8795e 100644
--- a/packages/frontend/src/pages/org-slug/projects/create/template/index.tsx
+++ b/packages/frontend/src/pages/org-slug/projects/create/template/index.tsx
@@ -1,15 +1,18 @@
import React, { useCallback, useEffect, useState } from 'react';
-import { useForm, Controller, SubmitHandler } from 'react-hook-form';
+import { useForm, SubmitHandler, Controller } from 'react-hook-form';
import { useNavigate, useOutletContext, useParams } from 'react-router-dom';
import toast from 'react-hot-toast';
import assert from 'assert';
-import { Button, Option, Typography } from '@material-tailwind/react';
-
import { useOctokit } from '../../../../../context/OctokitContext';
import { useGQLClient } from '../../../../../context/GQLClientContext';
-import AsyncSelect from '../../../../../components/shared/AsyncSelect';
import { Template } from '../../../../../types';
+import { Heading } from 'components/shared/Heading';
+import { Input } from 'components/shared/Input';
+import { Select, SelectOption } from 'components/shared/Select';
+import { ArrowRightCircleFilledIcon } from 'components/shared/CustomIcon';
+import { Checkbox } from 'components/shared/Checkbox';
+import { Button } from 'components/shared/Button';
type SubmitRepoValues = {
framework: string;
@@ -93,7 +96,7 @@ const CreateRepo = () => {
fetchUserAndOrgs();
}, [octokit]);
- const { register, handleSubmit, control, reset } = useForm({
+ const { handleSubmit, control, reset } = useForm({
defaultValues: {
framework: 'React',
repoName: '',
@@ -110,86 +113,67 @@ const CreateRepo = () => {
return (
);
diff --git a/packages/frontend/src/pages/org-slug/projects/id/Overview.tsx b/packages/frontend/src/pages/org-slug/projects/id/Overview.tsx
index 81cdbe16..c1337045 100644
--- a/packages/frontend/src/pages/org-slug/projects/id/Overview.tsx
+++ b/packages/frontend/src/pages/org-slug/projects/id/Overview.tsx
@@ -115,8 +115,8 @@ const OverviewTabPanel = () => {
}, [project]);
return (
-
-
+
+
{
imageSrc={project.icon}
type="blue"
/>
-
-
+
+
{project.name}
-
+
{project.subDomain}
-
+
}>
diff --git a/yarn.lock b/yarn.lock
index 081bebc1..4618b942 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -18218,7 +18218,7 @@ use-sync-external-store@1.2.0:
resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz#7dbefd6ef3fe4e767a0cf5d7287aacfb5846928a"
integrity sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==
-usehooks-ts@^2.10.0:
+usehooks-ts@^2.15.1:
version "2.15.1"
resolved "https://registry.yarnpkg.com/usehooks-ts/-/usehooks-ts-2.15.1.tgz#ede348c6f01b4b4fe981e240551624885a2fed83"
integrity sha512-AK29ODCt4FT9XleILNbkbjjmkRCNaQrgxQEkvqHjlnT76iPXzTFGvK2Y/s83JEdSxRp43YEnSa3bYBEV6HZ26Q==